diff options
994 files changed, 22758 insertions, 16170 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index c6ce799f0a24..0080a8d63bfc 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -81,6 +81,7 @@ aconfig_declarations_group { "android.view.inputmethod.flags-aconfig-java", "android.webkit.flags-aconfig-java", "android.widget.flags-aconfig-java", + "android.xr.flags-aconfig-java", "art_exported_aconfig_flags_lib", "backstage_power_flags_lib", "backup_flags_lib", @@ -791,6 +792,12 @@ java_aconfig_library { ], } +cc_aconfig_library { + name: "android.permission.flags-aconfig-cc", + aconfig_declarations: "android.permission.flags-aconfig", + host_supported: true, +} + // SQLite aconfig_declarations { name: "android.database.sqlite-aconfig", @@ -893,6 +900,20 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } +// XR +aconfig_declarations { + name: "android.xr.flags-aconfig", + package: "android.xr", + container: "system", + srcs: ["core/java/android/content/pm/xr.aconfig"], +} + +java_aconfig_library { + name: "android.xr.flags-aconfig-java", + aconfig_declarations: "android.xr.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + // android.app aconfig_declarations { name: "android.app.flags-aconfig", diff --git a/Android.bp b/Android.bp index d4776f53cfc4..252aeef079d2 100644 --- a/Android.bp +++ b/Android.bp @@ -150,6 +150,7 @@ filegroup { // etc. ":framework-javastream-protos", ":statslog-framework-java-gen", // FrameworkStatsLog.java + ":statslog-hwui-java-gen", // HwuiStatsLog.java ":audio_policy_configuration_V7_0", ], } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 03a3a0d51891..46cc3f01d261 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -498,14 +498,6 @@ public final class QuotaController extends StateController { private long mEJGracePeriodTopAppMs = QcConstants.DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; - private long mQuotaBumpAdditionalDurationMs = - QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS; - private int mQuotaBumpAdditionalJobCount = QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT; - private int mQuotaBumpAdditionalSessionCount = - QcConstants.DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT; - private long mQuotaBumpWindowSizeMs = QcConstants.DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS; - private int mQuotaBumpLimit = QcConstants.DEFAULT_QUOTA_BUMP_LIMIT; - /** * List of system apps with the {@link android.Manifest.permission#INSTALL_PACKAGES} permission * granted for each user. @@ -1095,7 +1087,7 @@ public final class QuotaController extends StateController { // essentially run until they reach the maximum limit. if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) { return calculateTimeUntilQuotaConsumedLocked( - events, startMaxElapsed, maxExecutionTimeRemainingMs, false); + events, startMaxElapsed, maxExecutionTimeRemainingMs); } // Need to check both max time and period time in case one is less than the other. @@ -1104,9 +1096,9 @@ public final class QuotaController extends StateController { // bucket value. return Math.min( calculateTimeUntilQuotaConsumedLocked( - events, startMaxElapsed, maxExecutionTimeRemainingMs, false), + events, startMaxElapsed, maxExecutionTimeRemainingMs), calculateTimeUntilQuotaConsumedLocked( - events, startWindowElapsed, allowedTimeRemainingMs, true)); + events, startWindowElapsed, allowedTimeRemainingMs)); } /** @@ -1116,36 +1108,12 @@ public final class QuotaController extends StateController { * @param deadSpaceMs How much time can be allowed to count towards the quota */ private long calculateTimeUntilQuotaConsumedLocked(@NonNull List<TimedEvent> sessions, - final long windowStartElapsed, long deadSpaceMs, boolean allowQuotaBumps) { + final long windowStartElapsed, long deadSpaceMs) { long timeUntilQuotaConsumedMs = 0; long start = windowStartElapsed; - int numQuotaBumps = 0; - final long quotaBumpWindowStartElapsed = - sElapsedRealtimeClock.millis() - mQuotaBumpWindowSizeMs; final int numSessions = sessions.size(); - if (allowQuotaBumps) { - for (int i = numSessions - 1; i >= 0; --i) { - TimedEvent event = sessions.get(i); - - if (event instanceof QuotaBump) { - if (event.getEndTimeElapsed() >= quotaBumpWindowStartElapsed - && numQuotaBumps++ < mQuotaBumpLimit) { - deadSpaceMs += mQuotaBumpAdditionalDurationMs; - } else { - break; - } - } - } - } for (int i = 0; i < numSessions; ++i) { - TimedEvent event = sessions.get(i); - - if (event instanceof QuotaBump) { - continue; - } - - TimingSession session = (TimingSession) event; - + TimingSession session = (TimingSession) sessions.get(i); if (session.endTimeElapsed < windowStartElapsed) { // Outside of window. Ignore. continue; @@ -1330,41 +1298,15 @@ public final class QuotaController extends StateController { final long startWindowElapsed = nowElapsed - stats.windowSizeMs; final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS; int sessionCountInWindow = 0; - int numQuotaBumps = 0; - final long quotaBumpWindowStartElapsed = nowElapsed - mQuotaBumpWindowSizeMs; // The minimum time between the start time and the beginning of the events that were // looked at --> how much time the stats will be valid for. long emptyTimeMs = Long.MAX_VALUE; // Sessions are non-overlapping and in order of occurrence, so iterating backwards will get // the most recent ones. final int loopStart = events.size() - 1; - // Process QuotaBumps first to ensure the limits are properly adjusted. - for (int i = loopStart; i >= 0; --i) { - TimedEvent event = events.get(i); - - if (event.getEndTimeElapsed() < quotaBumpWindowStartElapsed - || numQuotaBumps >= mQuotaBumpLimit) { - break; - } - - if (event instanceof QuotaBump) { - stats.allowedTimePerPeriodMs += mQuotaBumpAdditionalDurationMs; - stats.jobCountLimit += mQuotaBumpAdditionalJobCount; - stats.sessionCountLimit += mQuotaBumpAdditionalSessionCount; - emptyTimeMs = Math.min(emptyTimeMs, - event.getEndTimeElapsed() - quotaBumpWindowStartElapsed); - numQuotaBumps++; - } - } TimingSession lastSeenTimingSession = null; for (int i = loopStart; i >= 0; --i) { - TimedEvent event = events.get(i); - - if (event instanceof QuotaBump) { - continue; - } - - TimingSession session = (TimingSession) event; + TimingSession session = (TimingSession) events.get(i); // Window management. if (startWindowElapsed < session.endTimeElapsed) { @@ -2058,28 +2000,6 @@ public final class QuotaController extends StateController { } @VisibleForTesting - static final class QuotaBump implements TimedEvent { - // Event timestamp in elapsed realtime timebase. - public final long eventTimeElapsed; - - QuotaBump(long eventElapsed) { - this.eventTimeElapsed = eventElapsed; - } - - @Override - public long getEndTimeElapsed() { - return eventTimeElapsed; - } - - @Override - public void dump(IndentingPrintWriter pw) { - pw.print("Quota bump @ "); - pw.print(eventTimeElapsed); - pw.println(); - } - } - - @VisibleForTesting static final class ShrinkableDebits { /** The amount of quota remaining. Can be negative if limit changes. */ private long mDebitTally; @@ -2528,21 +2448,6 @@ public final class QuotaController extends StateController { updateStandbyBucket(userId, packageName, bucketIndex); }); } - - @Override - public void triggerTemporaryQuotaBump(String packageName, @UserIdInt int userId) { - synchronized (mLock) { - List<TimedEvent> events = mTimingEvents.get(userId, packageName); - if (events == null || events.size() == 0) { - // If the app hasn't run any jobs, there's no point giving it a quota bump. - return; - } - events.add(new QuotaBump(sElapsedRealtimeClock.millis())); - invalidateAllExecutionStatsLocked(userId, packageName); - } - // Update jobs out of band. - mHandler.obtainMessage(MSG_CHECK_PACKAGE, userId, 0, packageName).sendToTarget(); - } } @VisibleForTesting @@ -3019,7 +2924,6 @@ public final class QuotaController extends StateController { mQcConstants.mRateLimitingConstantsUpdated = false; mQcConstants.mExecutionPeriodConstantsUpdated = false; mQcConstants.mEJLimitConstantsUpdated = false; - mQcConstants.mQuotaBumpConstantsUpdated = false; } @Override @@ -3046,7 +2950,6 @@ public final class QuotaController extends StateController { private boolean mRateLimitingConstantsUpdated = false; private boolean mExecutionPeriodConstantsUpdated = false; private boolean mEJLimitConstantsUpdated = false; - private boolean mQuotaBumpConstantsUpdated = false; /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */ private static final String QC_CONSTANT_PREFIX = "qc_"; @@ -3194,21 +3097,6 @@ public final class QuotaController extends StateController { @VisibleForTesting static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS = QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS = - QC_CONSTANT_PREFIX + "quota_bump_additional_duration_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = - QC_CONSTANT_PREFIX + "quota_bump_additional_job_count"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = - QC_CONSTANT_PREFIX + "quota_bump_additional_session_count"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_WINDOW_SIZE_MS = - QC_CONSTANT_PREFIX + "quota_bump_window_size_ms"; - @VisibleForTesting - static final String KEY_QUOTA_BUMP_LIMIT = - QC_CONSTANT_PREFIX + "quota_bump_limit"; private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = 10 * 60 * 1000L; // 10 minutes @@ -3281,11 +3169,6 @@ public final class QuotaController extends StateController { private static final long DEFAULT_EJ_REWARD_NOTIFICATION_SEEN_MS = 0; private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS; - private static final long DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS = 1 * MINUTE_IN_MILLIS; - private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT = 2; - private static final int DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = 1; - private static final long DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS = 8 * HOUR_IN_MILLIS; - private static final int DEFAULT_QUOTA_BUMP_LIMIT = 8; /** * How much time each app in the exempted bucket will have to run jobs within their standby @@ -3587,33 +3470,6 @@ public final class QuotaController extends StateController { */ public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS; - /** - * How much additional session duration to give an app for each accepted quota bump. - */ - public long QUOTA_BUMP_ADDITIONAL_DURATION_MS = DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS; - - /** - * How many additional regular jobs to give an app for each accepted quota bump. - */ - public int QUOTA_BUMP_ADDITIONAL_JOB_COUNT = DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT; - - /** - * How many additional sessions to give an app for each accepted quota bump. - */ - public int QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = - DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT; - - /** - * The rolling window size within which to accept and apply quota bump events. - */ - public long QUOTA_BUMP_WINDOW_SIZE_MS = DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS; - - /** - * The maximum number of quota bumps to accept and apply within the - * {@link #QUOTA_BUMP_WINDOW_SIZE_MS window}. - */ - public int QUOTA_BUMP_LIMIT = DEFAULT_QUOTA_BUMP_LIMIT; - public void processConstantLocked(@NonNull DeviceConfig.Properties properties, @NonNull String key) { switch (key) { @@ -3650,14 +3506,6 @@ public final class QuotaController extends StateController { updateEJLimitConstantsLocked(); break; - case KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS: - case KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT: - case KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT: - case KEY_QUOTA_BUMP_WINDOW_SIZE_MS: - case KEY_QUOTA_BUMP_LIMIT: - updateQuotaBumpConstantsLocked(); - break; - case KEY_MAX_JOB_COUNT_EXEMPTED: MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED); int newExemptedMaxJobCount = @@ -4156,65 +4004,6 @@ public final class QuotaController extends StateController { } } - private void updateQuotaBumpConstantsLocked() { - if (mQuotaBumpConstantsUpdated) { - return; - } - mQuotaBumpConstantsUpdated = true; - - // Query the values as an atomic set. - final DeviceConfig.Properties properties = DeviceConfig.getProperties( - DeviceConfig.NAMESPACE_JOB_SCHEDULER, - KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - KEY_QUOTA_BUMP_WINDOW_SIZE_MS, KEY_QUOTA_BUMP_LIMIT); - QUOTA_BUMP_ADDITIONAL_DURATION_MS = properties.getLong( - KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - DEFAULT_QUOTA_BUMP_ADDITIONAL_DURATION_MS); - QUOTA_BUMP_ADDITIONAL_JOB_COUNT = properties.getInt( - KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, DEFAULT_QUOTA_BUMP_ADDITIONAL_JOB_COUNT); - QUOTA_BUMP_ADDITIONAL_SESSION_COUNT = properties.getInt( - KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - DEFAULT_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT); - QUOTA_BUMP_WINDOW_SIZE_MS = properties.getLong( - KEY_QUOTA_BUMP_WINDOW_SIZE_MS, DEFAULT_QUOTA_BUMP_WINDOW_SIZE_MS); - QUOTA_BUMP_LIMIT = properties.getInt( - KEY_QUOTA_BUMP_LIMIT, DEFAULT_QUOTA_BUMP_LIMIT); - - // The window must be in the range [1 hour, 24 hours]. - long newWindowSizeMs = Math.max(HOUR_IN_MILLIS, - Math.min(MAX_PERIOD_MS, QUOTA_BUMP_WINDOW_SIZE_MS)); - if (mQuotaBumpWindowSizeMs != newWindowSizeMs) { - mQuotaBumpWindowSizeMs = newWindowSizeMs; - mShouldReevaluateConstraints = true; - } - // The limit must be nonnegative. - int newLimit = Math.max(0, QUOTA_BUMP_LIMIT); - if (mQuotaBumpLimit != newLimit) { - mQuotaBumpLimit = newLimit; - mShouldReevaluateConstraints = true; - } - // The job count must be nonnegative. - int newJobAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_JOB_COUNT); - if (mQuotaBumpAdditionalJobCount != newJobAddition) { - mQuotaBumpAdditionalJobCount = newJobAddition; - mShouldReevaluateConstraints = true; - } - // The session count must be nonnegative. - int newSessionAddition = Math.max(0, QUOTA_BUMP_ADDITIONAL_SESSION_COUNT); - if (mQuotaBumpAdditionalSessionCount != newSessionAddition) { - mQuotaBumpAdditionalSessionCount = newSessionAddition; - mShouldReevaluateConstraints = true; - } - // The additional duration must be in the range [0, 10 minutes]. - long newAdditionalDuration = Math.max(0, - Math.min(10 * MINUTE_IN_MILLIS, QUOTA_BUMP_ADDITIONAL_DURATION_MS)); - if (mQuotaBumpAdditionalDurationMs != newAdditionalDuration) { - mQuotaBumpAdditionalDurationMs = newAdditionalDuration; - mShouldReevaluateConstraints = true; - } - } - private void dump(IndentingPrintWriter pw) { pw.println(); pw.println("QuotaController:"); @@ -4277,15 +4066,6 @@ public final class QuotaController extends StateController { EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS).println(); pw.print(KEY_EJ_GRACE_PERIOD_TOP_APP_MS, EJ_GRACE_PERIOD_TOP_APP_MS).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - QUOTA_BUMP_ADDITIONAL_DURATION_MS).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, - QUOTA_BUMP_ADDITIONAL_JOB_COUNT).println(); - pw.print(KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, - QUOTA_BUMP_ADDITIONAL_SESSION_COUNT).println(); - pw.print(KEY_QUOTA_BUMP_WINDOW_SIZE_MS, QUOTA_BUMP_WINDOW_SIZE_MS).println(); - pw.print(KEY_QUOTA_BUMP_LIMIT, QUOTA_BUMP_LIMIT).println(); - pw.decreaseIndent(); } @@ -4503,31 +4283,6 @@ public final class QuotaController extends StateController { return mQcConstants; } - @VisibleForTesting - long getQuotaBumpAdditionDurationMs() { - return mQuotaBumpAdditionalDurationMs; - } - - @VisibleForTesting - int getQuotaBumpAdditionJobCount() { - return mQuotaBumpAdditionalJobCount; - } - - @VisibleForTesting - int getQuotaBumpAdditionSessionCount() { - return mQuotaBumpAdditionalSessionCount; - } - - @VisibleForTesting - int getQuotaBumpLimit() { - return mQuotaBumpLimit; - } - - @VisibleForTesting - long getQuotaBumpWindowSizeMs() { - return mQuotaBumpWindowSizeMs; - } - //////////////////////////// DATA DUMP ////////////////////////////// @NeverCompile // Avoid size overhead of debugging code. diff --git a/cmds/bootanimation/OWNERS b/cmds/bootanimation/OWNERS index b6fb007bea52..2eda44dabec3 100644 --- a/cmds/bootanimation/OWNERS +++ b/cmds/bootanimation/OWNERS @@ -1,3 +1,4 @@ dupin@google.com shanh@google.com jreck@google.com +rahulbanerjee@google.com
\ No newline at end of file diff --git a/core/api/current.txt b/core/api/current.txt index a131ea7d29a7..6f60378d73e3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5103,6 +5103,7 @@ package android.app { method public int noteProxyOpNoThrow(@NonNull String, @Nullable String, int, @Nullable String, @Nullable String); method @Nullable public static String permissionToOp(@NonNull String); method public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback); + method @FlaggedApi("android.permission.flags.sync_on_op_noted_api") public void setOnOpNotedCallback(@Nullable java.util.concurrent.Executor, @Nullable android.app.AppOpsManager.OnOpNotedCallback, int); method @Deprecated public int startOp(@NonNull String, int, @NonNull String); method public int startOp(@NonNull String, int, @Nullable String, @Nullable String, @Nullable String); method @Deprecated public int startOpNoThrow(@NonNull String, int, @NonNull String); @@ -5157,6 +5158,7 @@ package android.app { field public static final String OPSTR_WRITE_CONTACTS = "android:write_contacts"; field public static final String OPSTR_WRITE_EXTERNAL_STORAGE = "android:write_external_storage"; field public static final String OPSTR_WRITE_SETTINGS = "android:write_settings"; + field @FlaggedApi("android.permission.flags.sync_on_op_noted_api") public static final int OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC = 1; // 0x1 field public static final int WATCH_FOREGROUND_CHANGES = 1; // 0x1 } @@ -32780,6 +32782,8 @@ package android.os { public class Build { ctor public Build(); method @NonNull public static java.util.List<android.os.Build.Partition> getFingerprintedPartitions(); + method @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static int getMajorSdkVersion(int); + method @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static int getMinorSdkVersion(int); method public static String getRadioVersion(); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public static String getSerial(); field public static final String BOARD; @@ -32876,6 +32880,41 @@ package android.os { } @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL { + field public static final int BASE = 100000; // 0x186a0 + field public static final int BASE_1_1 = 200000; // 0x30d40 + field public static final int CUPCAKE = 300000; // 0x493e0 + field public static final int DONUT = 400000; // 0x61a80 + field public static final int ECLAIR = 500000; // 0x7a120 + field public static final int ECLAIR_0_1 = 600000; // 0x927c0 + field public static final int ECLAIR_MR1 = 700000; // 0xaae60 + field public static final int FROYO = 800000; // 0xc3500 + field public static final int GINGERBREAD = 900000; // 0xdbba0 + field public static final int GINGERBREAD_MR1 = 1000000; // 0xf4240 + field public static final int HONEYCOMB = 1100000; // 0x10c8e0 + field public static final int HONEYCOMB_MR1 = 1200000; // 0x124f80 + field public static final int HONEYCOMB_MR2 = 1300000; // 0x13d620 + field public static final int ICE_CREAM_SANDWICH = 1400000; // 0x155cc0 + field public static final int ICE_CREAM_SANDWICH_MR1 = 1500000; // 0x16e360 + field public static final int JELLY_BEAN = 1600000; // 0x186a00 + field public static final int JELLY_BEAN_MR1 = 1700000; // 0x19f0a0 + field public static final int JELLY_BEAN_MR2 = 1800000; // 0x1b7740 + field public static final int KITKAT = 1900000; // 0x1cfde0 + field public static final int KITKAT_WATCH = 2000000; // 0x1e8480 + field public static final int LOLLIPOP = 2100000; // 0x200b20 + field public static final int LOLLIPOP_MR1 = 2200000; // 0x2191c0 + field public static final int M = 2300000; // 0x231860 + field public static final int N = 2400000; // 0x249f00 + field public static final int N_MR1 = 2500000; // 0x2625a0 + field public static final int O = 2600000; // 0x27ac40 + field public static final int O_MR1 = 2700000; // 0x2932e0 + field public static final int P = 2800000; // 0x2ab980 + field public static final int Q = 2900000; // 0x2c4020 + field public static final int R = 3000000; // 0x2dc6c0 + field public static final int S = 3100000; // 0x2f4d60 + field public static final int S_V2 = 3200000; // 0x30d400 + field public static final int TIRAMISU = 3300000; // 0x325aa0 + field public static final int UPSIDE_DOWN_CAKE = 3400000; // 0x33e140 + field public static final int VANILLA_ICE_CREAM = 3500000; // 0x3567e0 } public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable { @@ -43834,8 +43873,8 @@ package android.telephony { field public static final String KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY = "5g_nr_ssrsrq_thresholds_int_array"; field public static final String KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY = "5g_nr_sssinr_thresholds_int_array"; field public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool"; - field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool"; - field @FlaggedApi("com.android.internal.telephony.flags.show_call_id_and_call_waiting_in_additional_settings_menu") public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool"; + field public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool"; + field public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool"; field public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call"; field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; @@ -43918,7 +43957,7 @@ package android.telephony { 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 @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_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"; field public static final String KEY_CI_ACTION_ON_SYS_UPDATE_BOOL = "ci_action_on_sys_update_bool"; @@ -45894,6 +45933,7 @@ package android.telephony { method public byte[] getPdu(); method public int getProtocolIdentifier(); method public String getPseudoSubject(); + method @FlaggedApi("com.android.internal.telephony.flags.support_sms_over_ims_apis") @Nullable public String getRecipientAddress(); method public String getServiceCenterAddress(); method public int getStatus(); method public int getStatusOnIcc(); @@ -45955,7 +45995,7 @@ package android.telephony { method @Nullable public String getMncString(); method @Deprecated public String getNumber(); method public int getPortIndex(); - method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities(); + method @NonNull public java.util.Set<java.lang.Integer> getServiceCapabilities(); method public int getSimSlotIndex(); method public int getSubscriptionId(); method public int getSubscriptionType(); @@ -46038,9 +46078,9 @@ package android.telephony { field public static final int PHONE_NUMBER_SOURCE_CARRIER = 2; // 0x2 field public static final int PHONE_NUMBER_SOURCE_IMS = 3; // 0x3 field public static final int PHONE_NUMBER_SOURCE_UICC = 1; // 0x1 - field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3 - field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2 - field @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1 + field public static final int SERVICE_CAPABILITY_DATA = 3; // 0x3 + field public static final int SERVICE_CAPABILITY_SMS = 2; // 0x2 + field public static final int SERVICE_CAPABILITY_VOICE = 1; // 0x1 field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0 field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1 field public static final int USAGE_SETTING_DATA_CENTRIC = 2; // 0x2 @@ -46289,8 +46329,8 @@ package android.telephony { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataEnabledForReason(int); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_BASIC_PHONE_STATE}) public boolean isDataRoamingEnabled(); - method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceSmsCapable(); - method @FlaggedApi("com.android.internal.telephony.flags.data_only_cellular_service") public boolean isDeviceVoiceCapable(); + method public boolean isDeviceSmsCapable(); + method public boolean isDeviceVoiceCapable(); method public boolean isEmergencyNumber(@NonNull String); method public boolean isHearingAidCompatibilitySupported(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isManualNetworkSelectionAllowed(); @@ -46349,7 +46389,7 @@ package android.telephony { field public static final String ACTION_MULTI_SIM_CONFIG_CHANGED = "android.telephony.action.MULTI_SIM_CONFIG_CHANGED"; field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED"; field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE"; - field @FlaggedApi("com.android.internal.telephony.flags.reset_mobile_network_settings") public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; + field public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; field public static final String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE"; field public static final String ACTION_SECRET_CODE = "android.telephony.action.SECRET_CODE"; field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION"; @@ -50929,6 +50969,7 @@ package android.view { method public android.view.Display.Mode[] getSupportedModes(); method @Deprecated public float[] getSupportedRefreshRates(); method @Deprecated public int getWidth(); + method @FlaggedApi("com.android.server.display.feature.flags.enable_has_arr_support") public boolean hasArrSupport(); method public boolean isHdr(); method public boolean isHdrSdrRatioAvailable(); method public boolean isMinimalPostProcessingSupported(); @@ -61669,6 +61710,8 @@ package android.window { public final class BackEvent { ctor public BackEvent(float, float, float, int); + ctor @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public BackEvent(float, float, float, int, long); + method @FlaggedApi("com.android.window.flags.predictive_back_timestamp_api") public long getFrameTime(); method @FloatRange(from=0, to=1) public float getProgress(); method public int getSwipeEdge(); method public float getTouchX(); diff --git a/core/api/removed.txt b/core/api/removed.txt index 3c7c0d6e6ea1..a3cab291a1de 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -82,12 +82,12 @@ package android.database { package android.graphics { @Deprecated public class AvoidXfermode extends android.graphics.Xfermode { - ctor public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode); + ctor @Deprecated public AvoidXfermode(int, int, android.graphics.AvoidXfermode.Mode); } - public enum AvoidXfermode.Mode { - enum_constant public static final android.graphics.AvoidXfermode.Mode AVOID; - enum_constant public static final android.graphics.AvoidXfermode.Mode TARGET; + @Deprecated public enum AvoidXfermode.Mode { + enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode AVOID; + enum_constant @Deprecated public static final android.graphics.AvoidXfermode.Mode TARGET; } public class Canvas { @@ -102,9 +102,9 @@ package android.graphics { } @Deprecated public class LayerRasterizer extends android.graphics.Rasterizer { - ctor public LayerRasterizer(); - method public void addLayer(android.graphics.Paint, float, float); - method public void addLayer(android.graphics.Paint); + ctor @Deprecated public LayerRasterizer(); + method @Deprecated public void addLayer(android.graphics.Paint, float, float); + method @Deprecated public void addLayer(android.graphics.Paint); } public class Paint { @@ -118,7 +118,7 @@ package android.graphics { } @Deprecated public class PixelXorXfermode extends android.graphics.Xfermode { - ctor public PixelXorXfermode(int); + ctor @Deprecated public PixelXorXfermode(int); } public class Rasterizer { @@ -170,14 +170,14 @@ package android.media.tv { package android.net { @Deprecated public class NetworkBadging { - method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); - field public static final int BADGING_4K = 30; // 0x1e - field public static final int BADGING_HD = 20; // 0x14 - field public static final int BADGING_NONE = 0; // 0x0 - field public static final int BADGING_SD = 10; // 0xa + method @Deprecated @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme); + field @Deprecated public static final int BADGING_4K = 30; // 0x1e + field @Deprecated public static final int BADGING_HD = 20; // 0x14 + field @Deprecated public static final int BADGING_NONE = 0; // 0x0 + field @Deprecated public static final int BADGING_SD = 10; // 0xa } - @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging { + @Deprecated @IntDef({0x0, 0xa, 0x14, 0x1e}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface NetworkBadging.Badging { } public final class Proxy { @@ -304,14 +304,14 @@ package android.provider { @Deprecated public static final class ContactsContract.RawContacts.StreamItems implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemsColumns { field @Deprecated public static final String CONTENT_DIRECTORY = "stream_items"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { field @Deprecated public static final String PHOTO = "photo"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated protected static interface ContactsContract.StreamItemPhotosColumns { @@ -332,16 +332,16 @@ package android.provider { field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item"; field @Deprecated public static final android.net.Uri CONTENT_URI; field @Deprecated public static final String MAX_ITEMS = "max_items"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated public static final class ContactsContract.StreamItems.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { field @Deprecated public static final String CONTENT_DIRECTORY = "photo"; field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/stream_item_photo"; field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/stream_item_photo"; - field public static final String _COUNT = "_count"; - field public static final String _ID = "_id"; + field @Deprecated public static final String _COUNT = "_count"; + field @Deprecated public static final String _ID = "_id"; } @Deprecated protected static interface ContactsContract.StreamItemsColumns { @@ -447,14 +447,14 @@ package android.text.style { package android.util { @Deprecated public class FloatMath { - method public static float ceil(float); - method public static float cos(float); - method public static float exp(float); - method public static float floor(float); - method public static float hypot(float, float); - method public static float pow(float, float); - method public static float sin(float); - method public static float sqrt(float); + method @Deprecated public static float ceil(float); + method @Deprecated public static float cos(float); + method @Deprecated public static float exp(float); + method @Deprecated public static float floor(float); + method @Deprecated public static float hypot(float, float); + method @Deprecated public static float pow(float, float); + method @Deprecated public static float sin(float); + method @Deprecated public static float sqrt(float); } } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 10206f245c8a..fa4fc43c3418 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -7319,6 +7319,7 @@ package android.media { field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1 + field @FlaggedApi("android.media.audio.muted_by_port_volume_api") @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_PORT_VOLUME = 64; // 0x40 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_MUTED = 4; // 0x4 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_STREAM_VOLUME = 2; // 0x2 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_VOLUME_SHAPER = 32; // 0x20 @@ -13104,7 +13105,7 @@ package android.service.notification { method public final void adjustNotification(@NonNull android.service.notification.Adjustment); method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>); method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int); - method @Deprecated public void onAllowedAdjustmentsChanged(); + method public void onAllowedAdjustmentsChanged(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); method public void onNotificationClicked(@NonNull String); method public void onNotificationDirectReplied(@NonNull String); @@ -15426,6 +15427,7 @@ package android.telephony { field public static final int EVENT_DATA_CONNECTION_STATE_CHANGED = 7; // 0x7 field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_DATA_ENABLED_CHANGED = 34; // 0x22 field public static final int EVENT_DISPLAY_INFO_CHANGED = 21; // 0x15 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40; // 0x28 field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_EMERGENCY_NUMBER_LIST_CHANGED = 25; // 0x19 field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED = 28; // 0x1c field @RequiresPermission(android.Manifest.permission.READ_CALL_LOG) public static final int EVENT_LEGACY_CALL_STATE_CHANGED = 36; // 0x24 @@ -15463,6 +15465,12 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onDataEnabledChanged(boolean, int); } + @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static interface TelephonyCallback.EmergencyCallbackModeListener { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeRestarted(int, @NonNull java.time.Duration, int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeStarted(int, @NonNull java.time.Duration, int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onCallbackModeStopped(int, int, int); + } + public static interface TelephonyCallback.LinkCapacityEstimateChangedListener { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onLinkCapacityEstimateChanged(@NonNull java.util.List<android.telephony.LinkCapacityEstimate>); } @@ -15724,6 +15732,8 @@ package android.telephony { field public static final int CELL_BROADCAST_RESULT_SUCCESS = 0; // 0x0 field public static final int CELL_BROADCAST_RESULT_UNKNOWN = -1; // 0xffffffff field public static final int CELL_BROADCAST_RESULT_UNSUPPORTED = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int EMERGENCY_CALLBACK_MODE_CALL = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int EMERGENCY_CALLBACK_MODE_SMS = 2; // 0x2 field public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; // 0x4 field public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; // 0x1 field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; // 0x3 @@ -15781,6 +15791,13 @@ package android.telephony { field public static final int SRVCC_STATE_HANDOVER_FAILED = 2; // 0x2 field public static final int SRVCC_STATE_HANDOVER_NONE = -1; // 0xffffffff field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4; // 0x4 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_NORMAL_SMS_SENT = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3; // 0x3 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_TIMER_EXPIRED = 5; // 0x5 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_UNKNOWN = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.emergency_callback_mode_notification") public static final int STOP_REASON_USER_ACTION = 6; // 0x6 field public static final int THERMAL_MITIGATION_RESULT_INVALID_STATE = 3; // 0x3 field public static final int THERMAL_MITIGATION_RESULT_MODEM_ERROR = 1; // 0x1 field public static final int THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE = 2; // 0x2 diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index bbfa0ec3f3c2..78b9994e8fa1 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -7,8 +7,8 @@ package android.app { } @Deprecated public abstract static class AppOpsManager.AppOpsCollector extends android.app.AppOpsManager.OnOpNotedCallback { - ctor public AppOpsManager.AppOpsCollector(); - method @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); + ctor @Deprecated public AppOpsManager.AppOpsCollector(); + method @Deprecated @NonNull public java.util.concurrent.Executor getAsyncNotedExecutor(); } public class Notification implements android.os.Parcelable { @@ -207,7 +207,7 @@ package android.service.translation { @Deprecated public static interface TranslationService.OnTranslationResultCallback { method @Deprecated public void onError(); - method public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); + method @Deprecated public void onTranslationSuccess(@NonNull android.view.translation.TranslationResponse); } } @@ -261,64 +261,64 @@ package android.telephony.ims { } @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable { - method public boolean containsKey(@NonNull String); - method @NonNull public android.os.PersistableBundle copyBundle(); - method public int describeContents(); - method public boolean getBoolean(@NonNull String, boolean); - method public int getInt(@NonNull String, int); - method @Nullable public String getString(@NonNull String); - method public long getVersion(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; - field public static final String IPTYPE_IPV4 = "IPV4"; - field public static final String IPTYPE_IPV6 = "IPV6"; - field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; - field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; - field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; - field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; - field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; - field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; - field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; - field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; - field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; - field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; - field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; - field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; - field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; - field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; - field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; - field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; - field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; - field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; - field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; - field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; - field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; - field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; - field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; - field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; - field public static final String SIP_TRANSPORT_TCP = "TCP"; - field public static final String SIP_TRANSPORT_UDP = "UDP"; - } - - public static final class SipDelegateImsConfiguration.Builder { - ctor public SipDelegateImsConfiguration.Builder(int); - ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); - method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + method @Deprecated public boolean containsKey(@NonNull String); + method @Deprecated @NonNull public android.os.PersistableBundle copyBundle(); + method @Deprecated public int describeContents(); + method @Deprecated public boolean getBoolean(@NonNull String, boolean); + method @Deprecated public int getInt(@NonNull String, int); + method @Deprecated @Nullable public String getString(@NonNull String); + method @Deprecated public long getVersion(); + method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int); + field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field @Deprecated public static final String IPTYPE_IPV4 = "IPV4"; + field @Deprecated public static final String IPTYPE_IPV6 = "IPV6"; + field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string"; + field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP"; + field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + @Deprecated public static final class SipDelegateImsConfiguration.Builder { + ctor @Deprecated public SipDelegateImsConfiguration.Builder(int); + ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); } } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5e4485c33233..1ff8c510b6bf 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -393,7 +393,9 @@ package android.app { public class NotificationManager { method @FlaggedApi("android.app.modes_api") @NonNull public String addAutomaticZenRule(@NonNull android.app.AutomaticZenRule, boolean); + method @FlaggedApi("android.service.notification.notification_classification") public void allowAssistantAdjustment(@NonNull String); method public void cleanUpCallersAfter(long); + method @FlaggedApi("android.service.notification.notification_classification") public void disallowAssistantAdjustment(@NonNull String); method @FlaggedApi("android.app.modes_api") @NonNull public android.service.notification.ZenPolicy getDefaultZenPolicy(); method public android.content.ComponentName getEffectsSuppressor(); method @FlaggedApi("android.service.notification.notification_classification") @NonNull public java.util.Set<java.lang.String> getUnsupportedAdjustmentTypes(); @@ -1099,6 +1101,7 @@ package android.content.pm { field public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L; // 0xfd27b38L field public static final long OVERRIDE_USE_DISPLAY_LANDSCAPE_NATURAL_ORIENTATION = 255940284L; // 0xf4156bcL field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2 + field public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // 0x15498ba7L } public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { diff --git a/core/java/Android.bp b/core/java/Android.bp index 1265de1ebb15..65cd984ac1ba 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -214,6 +214,9 @@ filegroup { aidl_interface { name: "android.os.hintmanager_aidl", + defaults: [ + "android.hardware.power-aidl", + ], srcs: [ "android/os/IHintManager.aidl", "android/os/IHintSession.aidl", @@ -231,9 +234,6 @@ aidl_interface { enabled: true, }, }, - imports: [ - "android.hardware.power-V5", - ], } aidl_library { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0c02ba44fddb..99625ac20e60 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3841,6 +3841,18 @@ public final class ActivityThread extends ClientTransactionHandler return activityRecord != null ? activityRecord.activity : null; } + /** + * Returns the most recent created activity that's still running. + */ + @Nullable + public Activity getLastCreatedActivity() { + if (mActivities.isEmpty()) { + return null; + } + + return mActivities.valueAt(mActivities.size() - 1).activity; + } + @Override public ActivityClientRecord getActivityClient(IBinder token) { return mActivities.get(token); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 0472ff8c9f50..2e3d22647a0f 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -264,6 +264,13 @@ public class AppOpsManager { private static @Nullable OnOpNotedCallback sOnOpNotedCallback; /** + * Whether OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC was set when sOnOpNotedCallback was registered + * last time. + */ + @GuardedBy("sLock") + private static boolean sIgnoreAsyncNotedCallback; + + /** * Sync note-ops collected from {@link #readAndLogNotedAppops(Parcel)} that have not been * delivered to a callback yet. * @@ -10111,6 +10118,22 @@ public class AppOpsManager { private static final int COLLECT_SYNC = 2; private static final int COLLECT_ASYNC = 3; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = { "OP_NOTED_CALLBACK_FLAG_" }, value = { + OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC, + }) + private @interface OpNotedCallbackFlags {} + + /** + * Ignores async op noted events. + * + * @see #setOnOpNotedCallback + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_SYNC_ON_OP_NOTED_API) + public static final int OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC = 1; + private static final int OP_NOTED_CALLBACK_FLAG_ALL = OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC; + /** * Mark an app-op as noted. */ @@ -10256,6 +10279,12 @@ public class AppOpsManager { * <p>There can only ever be one collector per process. If there currently is another callback * set, this will fail. * + * <p>Note that if an app has multiple processes registering for this callback, the system would + * fan out async op noted callbacks to each of the processes, resulting in the same data being + * delivered multiple times to an app, which is usually undesired. To avoid this, consider + * listening to async ops only in one process. See + * {@link #setOnOpNotedCallback(Executor, OnOpNotedCallback, int)} for how to do this. + * * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code * null} to unset * @param callback listener to set, {@code null} to unset @@ -10264,18 +10293,62 @@ public class AppOpsManager { */ public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor, @Nullable OnOpNotedCallback callback) { + setOnOpNotedCallback(asyncExecutor, callback, /* flag */ 0); + } + + /** + * Set a new {@link OnOpNotedCallback}. + * + * <p>There can only ever be one collector per process. If there currently is another callback + * set, this will fail. + * + * <p>This API allows the caller to listen only to sync and self op noted events, and ignore + * async ops. This is useful in the scenario where an app has multiple processes. Consider an + * example where an app has two processes, A and B. The op noted events are as follows: + * <ul> + * <li>op 1: process A, sync + * <li>op 2: process A, async + * <li>op 3: process B, sync + * <li>op 4: process B, async + * Any process that listens to async op noted events gets events originating from across ALL + * processes (op 2 and op 4 in this example). So if both process A and B register as listeners, + * both of them get op 2 and 4 which is not ideal. To avoid duplicates, one of the two processes + * should set {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC}. For example + * process A sets {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC} and would then only get its own + * sync event (op 1). The other process would then listen to all types of events and get op 2, 3 + * and 4. + * + * Note that even with {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC}, + * {@link #OnOpNotedCallback.onAsyncNoted} may still be invoked. This happens for sync events + * that were collected before a callback is registered. + * + * @param asyncExecutor executor to execute {@link OnOpNotedCallback#onAsyncNoted} on, {@code + * null} to unset + * @param callback listener to set, {@code null} to unset + * @param flags additional flags to modify the callback behavior, such as + * {@link #OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC} + * + * @throws IllegalStateException If another callback is already registered + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_SYNC_ON_OP_NOTED_API) + public void setOnOpNotedCallback(@Nullable @CallbackExecutor Executor asyncExecutor, + @Nullable OnOpNotedCallback callback, @OpNotedCallbackFlags int flags) { Preconditions.checkState((callback == null) == (asyncExecutor == null)); + Preconditions.checkFlagsArgument(flags, OP_NOTED_CALLBACK_FLAG_ALL); synchronized (sLock) { if (callback == null) { + Preconditions.checkFlagsArgument(flags, 0); Preconditions.checkState(sOnOpNotedCallback != null, "No callback is currently registered"); - try { - mService.stopWatchingAsyncNoted(mContext.getPackageName(), - sOnOpNotedCallback.mAsyncCb); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); + if (!sIgnoreAsyncNotedCallback) { + try { + mService.stopWatchingAsyncNoted(mContext.getPackageName(), + sOnOpNotedCallback.mAsyncCb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } sOnOpNotedCallback = null; @@ -10285,14 +10358,17 @@ public class AppOpsManager { callback.mAsyncExecutor = asyncExecutor; sOnOpNotedCallback = callback; + sIgnoreAsyncNotedCallback = (flags & OP_NOTED_CALLBACK_FLAG_IGNORE_ASYNC) != 0; List<AsyncNotedAppOp> missedAsyncOps = null; - try { - mService.startWatchingAsyncNoted(mContext.getPackageName(), - sOnOpNotedCallback.mAsyncCb); - missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName()); - } catch (RemoteException e) { - e.rethrowFromSystemServer(); + if (!sIgnoreAsyncNotedCallback) { + try { + mService.startWatchingAsyncNoted(mContext.getPackageName(), + sOnOpNotedCallback.mAsyncCb); + missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName()); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } // Copy pointer so callback can be dispatched out of lock @@ -10305,17 +10381,15 @@ public class AppOpsManager { () -> onOpNotedCallback.onAsyncNoted(asyncNotedAppOp)); } } - synchronized (this) { - int numMissedSyncOps = sUnforwardedOps.size(); - if (onOpNotedCallback != null) { - for (int i = 0; i < numMissedSyncOps; i++) { - final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i); - onOpNotedCallback.getAsyncNotedExecutor().execute( - () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp)); - } + int numMissedSyncOps = sUnforwardedOps.size(); + if (onOpNotedCallback != null) { + for (int i = 0; i < numMissedSyncOps; i++) { + final AsyncNotedAppOp syncNotedAppOp = sUnforwardedOps.get(i); + onOpNotedCallback.getAsyncNotedExecutor().execute( + () -> onOpNotedCallback.onAsyncNoted(syncNotedAppOp)); } - sUnforwardedOps.clear(); } + sUnforwardedOps.clear(); } } } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 3b2aab4591a5..a7597b4d566d 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -84,6 +84,8 @@ interface INotificationManager boolean isImportanceLocked(String pkg, int uid); List<String> getAllowedAssistantAdjustments(String pkg); + void allowAssistantAdjustment(String adjustmentType); + void disallowAssistantAdjustment(String adjustmentType); boolean shouldHideSilentStatusIcons(String callingPkg); void setHideSilentStatusIcons(boolean hide); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8b33417e0a79..4ef5b5163fef 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -11296,7 +11296,11 @@ public class Notification implements Parcelable * @see Segment */ public @NonNull ProgressStyle setProgressSegments(@NonNull List<Segment> progressSegments) { - mProgressSegments = new ArrayList<>(progressSegments.size()); + if (mProgressSegments == null) { + mProgressSegments = new ArrayList<>(); + } + mProgressSegments.clear(); + mProgressSegments.addAll(progressSegments); return this; } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 41abd68c9924..06bf67c1e86f 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1790,6 +1790,34 @@ public class NotificationManager { } } + /** + * @hide + */ + @TestApi + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void allowAssistantAdjustment(@NonNull String capability) { + INotificationManager service = getService(); + try { + service.allowAssistantAdjustment(capability); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + */ + @TestApi + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void disallowAssistantAdjustment(@NonNull String capability) { + INotificationManager service = getService(); + try { + service.disallowAssistantAdjustment(capability); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** @hide */ @TestApi public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String pkg) { diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java index 08ecced234a9..06d95f5270c3 100644 --- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java +++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java @@ -137,8 +137,10 @@ public class AppFunctionRuntimeMetadata extends GenericDocument { .TOKENIZER_TYPE_VERBATIM) .build()) .addProperty( - new AppSearchSchema.BooleanPropertyConfig.Builder(PROPERTY_ENABLED) + new AppSearchSchema.LongPropertyConfig.Builder(PROPERTY_ENABLED) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + AppSearchSchema.LongPropertyConfig.INDEXING_TYPE_RANGE) .build()) .addProperty( new AppSearchSchema.StringPropertyConfig.Builder( @@ -212,19 +214,14 @@ public class AppFunctionRuntimeMetadata extends GenericDocument { } /** - * Sets an indicator specifying if the function is enabled or not. This would override the - * default enabled state in the static metadata ({@link - * AppFunctionStaticMetadataHelper#STATIC_PROPERTY_ENABLED_BY_DEFAULT}). Sets this to null - * to clear the override. - * TODO(369683073) Replace the tristate Boolean with IntDef EnabledState. + * Sets an indicator specifying the function enabled state. */ @NonNull public Builder setEnabled(@EnabledState int enabledState) { if (enabledState != APP_FUNCTION_STATE_DEFAULT && enabledState != APP_FUNCTION_STATE_ENABLED && enabledState != APP_FUNCTION_STATE_DISABLED) { - throw new IllegalArgumentException( - "Value of EnabledState is unsupported."); + throw new IllegalArgumentException("Value of EnabledState is unsupported."); } setPropertyLong(PROPERTY_ENABLED, enabledState); return this; diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java index cfbe7416bf9d..3438cc861661 100644 --- a/core/java/android/app/contextualsearch/ContextualSearchManager.java +++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java @@ -102,6 +102,7 @@ public final class ContextualSearchManager { * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH. */ public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN"; + /** * Intent action for contextual search invocation. The app providing the contextual search * experience must add this intent filter action to the activity it wants to be launched. @@ -111,6 +112,14 @@ public final class ContextualSearchManager { public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH = "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH"; + /** + * System feature declaring that the device supports Contextual Search. + * + * @hide + */ + public static final String FEATURE_CONTEXTUAL_SEARCH = + "com.google.android.feature.CONTEXTUAL_SEARCH"; + /** Entrypoint to be used when a user long presses on the nav handle. */ public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1; /** Entrypoint to be used when a user long presses on the home button. */ diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index b13901721909..37fa9a26b91c 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -6,6 +6,14 @@ container: "system" # flag relates to. flag { + name: "notifications_redesign_app_icons" + namespace: "systemui" + description: "Notifications Redesign: Use app icons in notification rows (not to be confused with" + " notifications_use_app_icons, notifications_use_app_icon_in_row which are just experiments)." + bug: "371174789" +} + +flag { name: "modes_api" is_exported: true namespace: "systemui" @@ -48,6 +56,16 @@ flag { } flag { + name: "modes_hsum" + namespace: "systemui" + description: "Fixes for modes (and DND/Zen in general) with HSUM or secondary users" + bug: "366203070" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "api_tvextender" is_exported: true namespace: "systemui" @@ -251,7 +269,7 @@ flag { name: "api_rich_ongoing" is_exported: true namespace: "systemui" - description: "Guards new android.app.richongoingnotification api" + description: "[RONs] Guards new RON-related APIs, including Notification.ProgressStyle" bug: "337261753" } @@ -259,6 +277,13 @@ flag { name: "ui_rich_ongoing" is_exported: true namespace: "systemui" - description: "Guards new android.app.richongoingnotification promotion and new uis" - bug: "337261753" + description: "[RONs] Guards new promotion logic and UI, including AOD notification and Colorization" + bug: "367705002" } + +flag { + name: "backup_restore_logging" + namespace: "systemui" + description: "Adds logging for notification/modes backup and restore events" + bug: "289524803" +}
\ No newline at end of file diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 481e6b530162..ce52825ddb73 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -21,10 +21,12 @@ import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.TestApi; import android.app.Activity; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.EnabledSince; import android.compat.annotation.Overridable; import android.compat.annotation.UnsupportedAppUsage; @@ -1204,6 +1206,18 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public @interface SizeChangesSupportMode {} /** + * This change id makes the restriction of fixed orientation, aspect ratio, and resizability + * of the app to be ignored, which means making the app fill the given available area. + * @hide + */ + @ChangeId + @Overridable + @TestApi + @SuppressLint("UnflaggedApi") // @TestApi without associated public API. + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) + public static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415L; // buganizer id + + /** * This change id enables compat policy that ignores app requested orientation in * response to an app calling {@link android.app.Activity#setRequestedOrientation}. See * com.android.server.wm.LetterboxUiController#shouldIgnoreRequestedOrientation for diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index c7d7dc1eb0de..52d733314eb6 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -326,3 +326,11 @@ flag { bug: "360129103" is_fixed_read_only: true } + +flag { + name: "include_feature_flags_in_package_cacher" + namespace: "package_manager_service" + description: "Include feature flag status when determining hits or misses in PackageCacher." + bug: "364771256" + is_fixed_read_only: true +} diff --git a/core/java/android/content/pm/xr.aconfig b/core/java/android/content/pm/xr.aconfig new file mode 100644 index 000000000000..61835c162c49 --- /dev/null +++ b/core/java/android/content/pm/xr.aconfig @@ -0,0 +1,9 @@ +package: "android.xr" +container: "system" + +flag { + namespace: "xr" + name: "xr_manifest_entries" + description: "Adds manifest entries used by Android XR" + bug: "364416355" +}
\ No newline at end of file diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig index 99eeca98bce0..f03a4a954842 100644 --- a/core/java/android/hardware/input/input_framework.aconfig +++ b/core/java/android/hardware/input/input_framework.aconfig @@ -142,3 +142,10 @@ flag { description: "Controls whether the connected mice's primary buttons, left and right, can be swapped." bug: "352598211" } + +flag { + name: "keyboard_a11y_shortcut_control" + namespace: "input" + description: "Adds shortcuts to toggle and control a11y features" + bug: "373458181" +} diff --git a/core/java/android/hardware/radio/RadioAlert.aidl b/core/java/android/hardware/radio/RadioAlert.aidl new file mode 100644 index 000000000000..17f4fc7e9a13 --- /dev/null +++ b/core/java/android/hardware/radio/RadioAlert.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +/** @hide */ +parcelable RadioAlert;
\ No newline at end of file diff --git a/core/java/android/hardware/radio/RadioAlert.java b/core/java/android/hardware/radio/RadioAlert.java new file mode 100644 index 000000000000..b55dcd82ef7b --- /dev/null +++ b/core/java/android/hardware/radio/RadioAlert.java @@ -0,0 +1,505 @@ +/** + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +import android.annotation.FlaggedApi; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Emergency Alert Message + * + * <p>Alert message can be sent from a radio station of technologies such as HD radio to + * the radio users for some emergency events (see ITU-T X.1303 bis for more info). + * @hide + */ +@FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM) +public final class RadioAlert implements Parcelable { + + public static final class Geocode implements Parcelable { + + private final String mValueName; + private final String mValue; + + /** + * Constructor of geocode. + * + * @param valueName Name of geocode value + * @param value Value of geocode + * @hide + */ + public Geocode(@NonNull String valueName, @NonNull String value) { + mValueName = Objects.requireNonNull(valueName, "Geocode value name can not be null"); + mValue = Objects.requireNonNull(value, "Geocode value can not be null"); + } + + private Geocode(Parcel in) { + mValueName = in.readString8(); + mValue = in.readString8(); + } + + public static final @NonNull Creator<Geocode> CREATOR = new Creator<Geocode>() { + @Override + public Geocode createFromParcel(Parcel in) { + return new Geocode(in); + } + + @Override + public Geocode[] newArray(int size) { + return new Geocode[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mValueName); + dest.writeString8(mValue); + } + + @NonNull + @Override + public String toString() { + return "Gecode [valueName=" + mValueName + ", value=" + mValue + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mValueName, mValue); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Geocode other)) { + return false; + } + + return Objects.equals(mValueName, other.mValueName) + && Objects.equals(mValue, other.mValue); + } + } + + public static final class Coordinate implements Parcelable { + private final double mLatitude; + private final double mLongitude; + + /** + * Constructor of coordinate. + * + * @param latitude Latitude of the coordinate + * @param longitude Longitude of the coordinate + * @hide + */ + public Coordinate(double latitude, double longitude) { + if (latitude < -90.0 || latitude > 90.0) { + throw new IllegalArgumentException("Latitude value should be between -90 and 90"); + } + if (longitude < -180.0 || longitude > 180.0) { + throw new IllegalArgumentException( + "Longitude value should be between -180 and 180"); + } + mLatitude = latitude; + mLongitude = longitude; + } + + private Coordinate(Parcel in) { + mLatitude = in.readDouble(); + mLongitude = in.readDouble(); + } + + public static final @NonNull Creator<Coordinate> CREATOR = new Creator<Coordinate>() { + @Override + public Coordinate createFromParcel(Parcel in) { + return new Coordinate(in); + } + + @Override + public Coordinate[] newArray(int size) { + return new Coordinate[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeDouble(mLatitude); + dest.writeDouble(mLongitude); + } + + @NonNull + @Override + public String toString() { + return "Coordinate [latitude=" + mLatitude + ", longitude=" + mLongitude + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mLatitude, mLongitude); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Coordinate other)) { + return false; + } + return mLatitude == other.mLatitude && mLongitude == other.mLongitude; + } + } + + public static final class Polygon implements Parcelable { + + private final List<Coordinate> mCoordinates; + + /** + * Constructor of polygon. + * + * @param coordinates Coordinates the polygon is composed of + * @hide + */ + public Polygon(@NonNull List<Coordinate> coordinates) { + Objects.requireNonNull(coordinates, "Coordinates can not be null"); + if (coordinates.size() < 4) { + throw new IllegalArgumentException("Number of coordinates must be at least 4"); + } + if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) { + throw new IllegalArgumentException( + "The last and first coordinates must be the same"); + } + mCoordinates = coordinates; + } + + private Polygon(Parcel in) { + mCoordinates = new ArrayList<>(); + in.readTypedList(mCoordinates, Coordinate.CREATOR); + } + + public static final @NonNull Creator<Polygon> CREATOR = new Creator<Polygon>() { + @Override + public Polygon createFromParcel(Parcel in) { + return new Polygon(in); + } + + @Override + public Polygon[] newArray(int size) { + return new Polygon[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mCoordinates); + } + + @NonNull + @Override + public String toString() { + return "Polygon [coordinates=" + mCoordinates + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mCoordinates); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Polygon other)) { + return false; + } + return mCoordinates.equals(other.mCoordinates); + } + } + + public static final class AlertArea implements Parcelable { + + private final List<Polygon> mPolygons; + private final List<Geocode> mGeocodes; + + /** + * Constructor of alert area. + * + * @param polygons Polygons used in alert area + * @param geocodes Geocodes used in alert area + * @hide + */ + public AlertArea(@NonNull List<Polygon> polygons, @NonNull List<Geocode> geocodes) { + mPolygons = Objects.requireNonNull(polygons, "Polygons can not be null"); + mGeocodes = Objects.requireNonNull(geocodes, "Geocodes can not be null"); + } + + private AlertArea(Parcel in) { + mPolygons = new ArrayList<>(); + mGeocodes = new ArrayList<>(); + in.readTypedList(mPolygons, Polygon.CREATOR); + in.readTypedList(mGeocodes, Geocode.CREATOR); + } + + public static final @NonNull Creator<AlertArea> CREATOR = new Creator<AlertArea>() { + @Override + public AlertArea createFromParcel(Parcel in) { + return new AlertArea(in); + } + + @Override + public AlertArea[] newArray(int size) { + return new AlertArea[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mPolygons); + dest.writeTypedList(mGeocodes); + } + + @NonNull + @Override + public String toString() { + return "AlertArea [polygons=" + mPolygons + ", geocodes=" + mGeocodes + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mPolygons, mGeocodes); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AlertArea other)) { + return false; + } + + return mPolygons.equals(other.mPolygons) && mGeocodes.equals(other.mGeocodes); + } + } + + public static final class AlertInfo implements Parcelable { + + private final List<Integer> mCategoryList; + private final int mUrgency; + private final int mSeverity; + private final int mCertainty; + private final String mTextualMessage; + private final List<AlertArea> mAreaList; + + /** + * Constructor for alert info. + * + * @param categoryList Array of categories of the subject event of the alert message + * @param urgency The urgency of the subject event of the alert message + * @param severity The severity of the subject event of the alert message + * @param certainty The certainty of the subject event of the alert message + * @param textualMessage Textual descriptions of the subject event + * @param areaList The array of geographic areas to which the alert info segment in which + * it appears applies + * @hide + */ + public AlertInfo(@NonNull List<Integer> categoryList, int urgency, + int severity, int certainty, + String textualMessage, @NonNull List<AlertArea> areaList) { + mCategoryList = Objects.requireNonNull(categoryList, "Category list can not be null"); + mUrgency = urgency; + mSeverity = severity; + mCertainty = certainty; + mTextualMessage = textualMessage; + mAreaList = Objects.requireNonNull(areaList, "Area list can not be null"); + } + + private AlertInfo(Parcel in) { + mCategoryList = in.readArrayList(Integer.class.getClassLoader(), Integer.class); + mUrgency = in.readInt(); + mSeverity = in.readInt(); + mCertainty = in.readInt(); + mTextualMessage = in.readString8(); + mAreaList = new ArrayList<>(); + in.readTypedList(mAreaList, AlertArea.CREATOR); + } + + public static final @NonNull Creator<AlertInfo> CREATOR = new Creator<AlertInfo>() { + @Override + public AlertInfo createFromParcel(Parcel in) { + return new AlertInfo(in); + } + + @Override + public AlertInfo[] newArray(int size) { + return new AlertInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeList(mCategoryList); + dest.writeInt(mUrgency); + dest.writeInt(mSeverity); + dest.writeInt(mCertainty); + dest.writeString8(mTextualMessage); + dest.writeTypedList(mAreaList); + } + + @NonNull + @Override + public String toString() { + return "AlertInfo [categoryList=" + mCategoryList + ", urgency=" + mUrgency + + ", severity=" + mSeverity + ", certainty=" + mCertainty + + ", textualMessage=" + mTextualMessage + ", areaList=" + mAreaList + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mCategoryList, mUrgency, mSeverity, mCertainty, mTextualMessage, + mAreaList); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AlertInfo other)) { + return false; + } + + return mCategoryList.equals(other.mCategoryList) && mUrgency == other.mUrgency + && mSeverity == other.mSeverity && mCertainty == other.mCertainty + && mTextualMessage.equals(other.mTextualMessage) + && mAreaList.equals(other.mAreaList); + } + } + + private final int mStatus; + private final int mMessageType; + private final List<AlertInfo> mInfoList; + private final int mScope; + + /** + * Constructor of radio alert message. + * + * @param status Status of alert message + * @param messageType Message type of alert message + * @param infoList List of alert info + * @param scope Scope of alert message + * @hide + */ + public RadioAlert(int status, int messageType, + @NonNull List<AlertInfo> infoList, int scope) { + mStatus = status; + mMessageType = messageType; + mInfoList = Objects.requireNonNull(infoList, "Alert info list can not be null"); + mScope = scope; + } + + private RadioAlert(Parcel in) { + mStatus = in.readInt(); + mMessageType = in.readInt(); + mInfoList = in.readParcelableList(new ArrayList<>(), AlertInfo.class.getClassLoader(), + AlertInfo.class); + mScope = in.readInt(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStatus); + dest.writeInt(mMessageType); + dest.writeParcelableList(mInfoList, /* flags= */ 0); + dest.writeInt(mScope); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + @Override + public String toString() { + return "RadioAlert [status=" + mStatus + ", messageType=" + mMessageType + + ", infoList= " + mInfoList + ", scope=" + mScope + "]"; + } + + @Override + public int hashCode() { + return Objects.hash(mStatus, mMessageType, mInfoList, mScope); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof RadioAlert other)) { + return false; + } + + return mStatus == other.mStatus && mMessageType == other.mMessageType + && mInfoList.equals(other.mInfoList) && mScope == other.mScope; + } + + public static final @NonNull Creator<RadioAlert> CREATOR = new Creator<RadioAlert>() { + @Override + public RadioAlert createFromParcel(Parcel in) { + return new RadioAlert(in); + } + + @Override + public RadioAlert[] newArray(int size) { + return new RadioAlert[size]; + } + }; +} diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index dcb363ccf535..efddd1f9fc6f 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -21,38 +21,4 @@ flag{ namespace: "vcn" description: "Feature flag for enabling network metric monitor" bug: "282996138" -} - -flag{ - name: "validate_network_on_ipsec_loss" - namespace: "vcn" - description: "Trigger network validation when IPsec packet loss exceeds the threshold" - bug: "329139898" -} - -flag{ - name: "evaluate_ipsec_loss_on_lp_nc_change" - namespace: "vcn" - description: "Re-evaluate IPsec packet loss on LinkProperties or NetworkCapabilities change" - bug: "323238888" -} - -flag{ - name: "enforce_main_user" - namespace: "vcn" - description: "Enforce main user to make VCN HSUM compatible" - bug: "310310661" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag{ - name: "handle_seq_num_leap" - namespace: "vcn" - description: "Do not report bad network when there is a suspected sequence number leap" - bug: "332598276" - metadata { - purpose: PURPOSE_BUGFIX - } }
\ No newline at end of file diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 479aa98f80de..a89483394611 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -18,6 +18,7 @@ package android.os; import android.Manifest; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -43,6 +44,8 @@ import com.android.internal.ravenwood.RavenwoodEnvironment; import dalvik.system.VMRuntime; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -1273,6 +1276,47 @@ public class Build { public static final int VANILLA_ICE_CREAM = 35; } + /** @hide */ + @IntDef(value = { + VERSION_CODES_FULL.BASE, + VERSION_CODES_FULL.BASE_1_1, + VERSION_CODES_FULL.CUPCAKE, + VERSION_CODES_FULL.DONUT, + VERSION_CODES_FULL.ECLAIR, + VERSION_CODES_FULL.ECLAIR_0_1, + VERSION_CODES_FULL.ECLAIR_MR1, + VERSION_CODES_FULL.FROYO, + VERSION_CODES_FULL.GINGERBREAD, + VERSION_CODES_FULL.GINGERBREAD_MR1, + VERSION_CODES_FULL.HONEYCOMB, + VERSION_CODES_FULL.HONEYCOMB_MR1, + VERSION_CODES_FULL.HONEYCOMB_MR2, + VERSION_CODES_FULL.ICE_CREAM_SANDWICH, + VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1, + VERSION_CODES_FULL.JELLY_BEAN, + VERSION_CODES_FULL.JELLY_BEAN_MR1, + VERSION_CODES_FULL.JELLY_BEAN_MR2, + VERSION_CODES_FULL.KITKAT, + VERSION_CODES_FULL.KITKAT_WATCH, + VERSION_CODES_FULL.LOLLIPOP, + VERSION_CODES_FULL.LOLLIPOP_MR1, + VERSION_CODES_FULL.M, + VERSION_CODES_FULL.N, + VERSION_CODES_FULL.N_MR1, + VERSION_CODES_FULL.O, + VERSION_CODES_FULL.O_MR1, + VERSION_CODES_FULL.P, + VERSION_CODES_FULL.Q, + VERSION_CODES_FULL.R, + VERSION_CODES_FULL.S, + VERSION_CODES_FULL.S_V2, + VERSION_CODES_FULL.TIRAMISU, + VERSION_CODES_FULL.UPSIDE_DOWN_CAKE, + VERSION_CODES_FULL.VANILLA_ICE_CREAM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SdkIntFull {} + /** * Enumeration of the currently known SDK major and minor version codes. * The numbers increase for every release, and are guaranteed to be ordered @@ -1290,6 +1334,208 @@ public class Build { // Use the last 5 digits for the minor version. This allows the // minor version to be set to CUR_DEVELOPMENT. private static final int SDK_INT_MULTIPLIER = 100000; + + /** + * Android 1.0. + */ + public static final int BASE = VERSION_CODES.BASE * SDK_INT_MULTIPLIER; + + /** + * Android 2.0. + */ + public static final int BASE_1_1 = VERSION_CODES.BASE_1_1 * SDK_INT_MULTIPLIER; + + /** + * Android 3.0. + */ + public static final int CUPCAKE = VERSION_CODES.CUPCAKE * SDK_INT_MULTIPLIER; + + /** + * Android 4.0. + */ + public static final int DONUT = VERSION_CODES.DONUT * SDK_INT_MULTIPLIER; + + /** + * Android 5.0. + */ + public static final int ECLAIR = VERSION_CODES.ECLAIR * SDK_INT_MULTIPLIER; + + /** + * Android 6.0. + */ + public static final int ECLAIR_0_1 = VERSION_CODES.ECLAIR_0_1 * SDK_INT_MULTIPLIER; + + /** + * Android 7.0. + */ + public static final int ECLAIR_MR1 = VERSION_CODES.ECLAIR_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 8.0. + */ + public static final int FROYO = VERSION_CODES.FROYO * SDK_INT_MULTIPLIER; + + /** + * Android 9.0. + */ + public static final int GINGERBREAD = VERSION_CODES.GINGERBREAD * SDK_INT_MULTIPLIER; + + /** + * Android 10.0. + */ + public static final int GINGERBREAD_MR1 = + VERSION_CODES.GINGERBREAD_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 11.0. + */ + public static final int HONEYCOMB = VERSION_CODES.HONEYCOMB * SDK_INT_MULTIPLIER; + + /** + * Android 12.0. + */ + public static final int HONEYCOMB_MR1 = VERSION_CODES.HONEYCOMB_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 13.0. + */ + public static final int HONEYCOMB_MR2 = VERSION_CODES.HONEYCOMB_MR2 * SDK_INT_MULTIPLIER; + + /** + * Android 14.0. + */ + public static final int ICE_CREAM_SANDWICH = + VERSION_CODES.ICE_CREAM_SANDWICH * SDK_INT_MULTIPLIER; + + /** + * Android 15.0. + */ + public static final int ICE_CREAM_SANDWICH_MR1 = + VERSION_CODES.ICE_CREAM_SANDWICH_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 16.0. + */ + public static final int JELLY_BEAN = VERSION_CODES.JELLY_BEAN * SDK_INT_MULTIPLIER; + + /** + * Android 17.0. + */ + public static final int JELLY_BEAN_MR1 = VERSION_CODES.JELLY_BEAN_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 18.0. + */ + public static final int JELLY_BEAN_MR2 = VERSION_CODES.JELLY_BEAN_MR2 * SDK_INT_MULTIPLIER; + + /** + * Android 19.0. + */ + public static final int KITKAT = VERSION_CODES.KITKAT * SDK_INT_MULTIPLIER; + + /** + * Android 20.0. + */ + public static final int KITKAT_WATCH = VERSION_CODES.KITKAT_WATCH * SDK_INT_MULTIPLIER; + + /** + * Android 21.0. + */ + public static final int LOLLIPOP = VERSION_CODES.LOLLIPOP * SDK_INT_MULTIPLIER; + + /** + * Android 22.0. + */ + public static final int LOLLIPOP_MR1 = VERSION_CODES.LOLLIPOP_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 23.0. + */ + public static final int M = VERSION_CODES.M * SDK_INT_MULTIPLIER; + + /** + * Android 24.0. + */ + public static final int N = VERSION_CODES.N * SDK_INT_MULTIPLIER; + + /** + * Android 25.0. + */ + public static final int N_MR1 = VERSION_CODES.N_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 26.0. + */ + public static final int O = VERSION_CODES.O * SDK_INT_MULTIPLIER; + + /** + * Android 27.0. + */ + public static final int O_MR1 = VERSION_CODES.O_MR1 * SDK_INT_MULTIPLIER; + + /** + * Android 28.0. + */ + public static final int P = VERSION_CODES.P * SDK_INT_MULTIPLIER; + + /** + * Android 29.0. + */ + public static final int Q = VERSION_CODES.Q * SDK_INT_MULTIPLIER; + + /** + * Android 30.0. + */ + public static final int R = VERSION_CODES.R * SDK_INT_MULTIPLIER; + + /** + * Android 31.0. + */ + public static final int S = VERSION_CODES.S * SDK_INT_MULTIPLIER; + + /** + * Android 32.0. + */ + public static final int S_V2 = VERSION_CODES.S_V2 * SDK_INT_MULTIPLIER; + + /** + * Android 33.0. + */ + public static final int TIRAMISU = VERSION_CODES.TIRAMISU * SDK_INT_MULTIPLIER; + + /** + * Android 34.0. + */ + public static final int UPSIDE_DOWN_CAKE = + VERSION_CODES.UPSIDE_DOWN_CAKE * SDK_INT_MULTIPLIER; + + /** + * Android 35.0. + */ + public static final int VANILLA_ICE_CREAM = + VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER; + } + + /** + * Obtain the major version encoded in a VERSION_CODES_FULL value. + * This value is guaranteed to be non-negative. + * + * @return The major version encoded in a VERSION_CODES_FULL value + */ + @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) + public static int getMajorSdkVersion(@SdkIntFull int sdkIntFull) { + return sdkIntFull / VERSION_CODES_FULL.SDK_INT_MULTIPLIER; + } + + /** + * Obtain the minor version encoded in a VERSION_CODES_FULL value. + * This value is guaranteed to be non-negative. + * + * @return The minor version encoded in a VERSION_CODES_FULL value + */ + @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME) + public static int getMinorSdkVersion(@SdkIntFull int sdkIntFull) { + return sdkIntFull % VERSION_CODES_FULL.SDK_INT_MULTIPLIER; } /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f1964e7bc024..bd3da0d56cbd 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -66,6 +66,7 @@ import android.provider.Settings; import android.util.AndroidException; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -3908,11 +3909,57 @@ public class UserManager { android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true) public @NonNull UserProperties getUserProperties(@NonNull UserHandle userHandle) { final int userId = userHandle.getIdentifier(); - // Avoid calling into system server for invalid user ids. - if (android.multiuser.Flags.fixGetUserPropertyCache() && userId < 0) { + + if (userId < 0 && android.multiuser.Flags.fixGetUserPropertyCache()) { + // Avoid calling into system server for invalid user ids. throw new IllegalArgumentException("Cannot access properties for user " + userId); } - return mUserPropertiesCache.query(userId); + + if (!android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly() || userId < 0) { + // This is the historical code path, when all flags are false. + try { + return mService.getUserPropertiesCopy(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + final int callingUid = Binder.getCallingUid(); + final int processUid = Process.myUid(); + if (Build.isDebuggable() && callingUid != processUid) { + Log.w(TAG, "Uid " + processUid + " is fetching a copy of UserProperties on" + + " behalf of callingUid " + callingUid + ". Possibly" + + " it should carefully first clearCallingIdentity or perhaps use" + + " UserManagerInternal.getUserProperties() instead?", + new Throwable()); + } + + return getUserPropertiesFromQuery(new QueryUserId(userId)); + } + + /** @hide */ + public static final void invalidateUserPropertiesCache() { + UserManagerCache.invalidateUserPropertiesFromQuery(); + } + + /** + * Cachable version of {@link #getUserProperties(UserHandle)}, caching the UserProperties + * corresponding to the given QueryUserId. The cached copy depends on both the queried userId as + * well as the Binder caller querying it, since the result depends on the caller's permissions. + */ + @CachedProperty() + private @NonNull UserProperties getUserPropertiesFromQuery(QueryUserId query) { + return ((UserManagerCache) mIpcDataCache).getUserPropertiesFromQuery( + (QueryUserId q) -> mService.getUserPropertiesCopy(q.getUserId()), query); + } + + /** Class keeping track of a userId, as well as the callingUid that is asking about it. */ + /** @hide */ + static final class QueryUserId extends Pair<Integer, Integer> { + public QueryUserId(@UserIdInt int userId) { + super(Binder.getCallingUid(), userId); + } + public @UserIdInt int getUserId() { return second; } } /** @@ -6656,34 +6703,6 @@ public class UserManager { PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STATIC_USER_PROPERTIES); } - /* Cache key for UserProperties object. */ - private static final String CACHE_KEY_USER_PROPERTIES = - PropertyInvalidatedCache.createPropertyName( - PropertyInvalidatedCache.MODULE_SYSTEM, "user_properties"); - - // TODO: It would be better to somehow have this as static, so that it can work cross-context. - private final PropertyInvalidatedCache<Integer, UserProperties> mUserPropertiesCache = - new PropertyInvalidatedCache<Integer, UserProperties>(16, CACHE_KEY_USER_PROPERTIES) { - @Override - public UserProperties recompute(Integer userId) { - try { - // If the userId doesn't exist, this will throw rather than cache garbage. - return mService.getUserPropertiesCopy(userId); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - @Override - public boolean bypass(Integer query) { - return query < 0; - } - }; - - /** @hide */ - public static final void invalidateUserPropertiesCache() { - PropertyInvalidatedCache.invalidateCache(CACHE_KEY_USER_PROPERTIES); - } - /** * @hide * User that enforces a restriction. diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 81987907452f..c7cc653f4178 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -2,43 +2,74 @@ package: "android.os" container: "system" container: "system" +# keep-sorted start block=yes newline_separated=yes flag { - name: "android_os_build_vanilla_ice_cream" + name: "adpf_gpu_report_actual_work_duration" is_exported: true - namespace: "build" - description: "Feature flag for adding the VANILLA_ICE_CREAM constant." - bug: "264658905" + namespace: "game" + description: "Guards the ADPF GPU APIs." + bug: "284324521" } flag { - name: "state_of_health_public" - is_exported: true - namespace: "system_sw_battery" - description: "Feature flag for making state_of_health a public api." - bug: "288842045" + name: "adpf_hwui_gpu" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF" + is_fixed_read_only: true + bug: "330922490" } flag { - name: "disallow_cellular_null_ciphers_restriction" - namespace: "cellular_security" - description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices." - bug: "276752881" + name: "adpf_measure_during_input_event_boost" + namespace: "game" + description: "Guards use of a boost when view measures during input events" + bug: "256549451" } flag { - name: "remove_app_profiler_pss_collection" - is_exported: true - namespace: "backstage_power" - description: "Replaces background PSS collection in AppProfiler with RSS" - bug: "297542292" + name: "adpf_obtainview_boost" + namespace: "game" + description: "Guards use of a boost in response to HWUI obtainView" + is_fixed_read_only: true + bug: "328238660" } flag { - name: "allow_thermal_headroom_thresholds" + name: "adpf_platform_power_efficiency" + namespace: "game" + description: "Guards use of the ADPF power efficiency API within the platform" + is_fixed_read_only: true + bug: "277285195" +} + +flag { + name: "adpf_prefer_power_efficiency" is_exported: true namespace: "game" - description: "Enable thermal headroom thresholds API" - bug: "288119641" + description: "Guards the ADPF power efficiency API" + bug: "288117936" +} + +flag { + name: "adpf_use_fmq_channel" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF" + bug: "315894228" +} + +flag { + name: "adpf_use_fmq_channel_fixed" + namespace: "game" + description: "Guards use of the FMQ channel for ADPF with a readonly flag" + is_fixed_read_only: true + bug: "315894228" +} + +flag { + name: "allow_consentless_bugreport_delegated_consent" + namespace: "crumpet" + description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead" + bug: "324046728" } # This flag guards the private space feature, its APIs, and some of the feature implementations. The flag android.multiuser.Flags.enable_private_space_features exclusively guards all the implementations. @@ -52,27 +83,36 @@ flag { } flag { - name: "adpf_prefer_power_efficiency" + name: "allow_thermal_headroom_thresholds" is_exported: true namespace: "game" - description: "Guards the ADPF power efficiency API" - bug: "288117936" + description: "Enable thermal headroom thresholds API" + bug: "288119641" } flag { - name: "security_state_service" + name: "android_os_build_vanilla_ice_cream" is_exported: true - namespace: "dynamic_spl" - description: "Guards the Security State API." - bug: "302189431" + namespace: "build" + description: "Feature flag for adding the VANILLA_ICE_CREAM constant." + bug: "264658905" } flag { - name: "ordered_broadcast_multiple_permissions" + name: "api_for_backported_fixes" + namespace: "media_reliability" + description: "Public API app developers use to check if a known issue is fixed on a device." + bug: "308461809" is_exported: true - namespace: "bluetooth" - description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API" - bug: "345802719" +} + +flag { + name: "battery_part_status_api" + is_exported: true + namespace: "phoenix" + description: "Feature flag for adding Health HAL v3 APIs." + is_fixed_read_only: true + bug: "309792384" } flag { @@ -84,97 +124,98 @@ flag { } flag { - name: "adpf_gpu_report_actual_work_duration" - is_exported: true - namespace: "game" - description: "Guards the ADPF GPU APIs." - bug: "284324521" + name: "battery_service_support_current_adb_command" + namespace: "backstage_power" + description: "Whether or not BatteryService supports adb commands for Current values." + is_fixed_read_only: true + bug: "315037695" } flag { - name: "adpf_use_fmq_channel" - namespace: "game" - description: "Guards use of the FMQ channel for ADPF" - bug: "315894228" + name: "binder_frozen_state_change_callback" + is_exported: true + namespace: "system_performance" + description: "Guards the frozen state change callback API." + bug: "361157077" } flag { - name: "adpf_use_fmq_channel_fixed" - namespace: "game" - description: "Guards use of the FMQ channel for ADPF with a readonly flag" - is_fixed_read_only: true - bug: "315894228" + name: "disallow_cellular_null_ciphers_restriction" + namespace: "cellular_security" + description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices." + bug: "276752881" } flag { - name: "adpf_hwui_gpu" - namespace: "game" - description: "Guards use of the FMQ channel for ADPF" + name: "enable_angle_allow_list" + namespace: "gpu" + description: "Whether to read from angle allowlist to determine if app should use ANGLE" is_fixed_read_only: true - bug: "330922490" + bug: "370845648" } flag { - name: "adpf_obtainview_boost" - namespace: "game" - description: "Guards use of a boost in response to HWUI obtainView" - is_fixed_read_only: true - bug: "328238660" + name: "get_private_space_settings" + namespace: "profile_experiences" + description: "Guards a new Private Profile API in LauncherApps" + bug: "346294653" + is_exported: true } flag { - name: "adpf_platform_power_efficiency" - namespace: "game" - description: "Guards use of the ADPF power efficiency API within the platform" - is_fixed_read_only: true - bug: "277285195" + name: "mainline_vcn_platform_api" + namespace: "vcn" + description: "Expose platform APIs to mainline VCN" + is_exported: true + bug: "366598445" } flag { - name: "adpf_measure_during_input_event_boost" - namespace: "game" - description: "Guards use of a boost when view measures during input events" - bug: "256549451" + name: "message_queue_tail_tracking" + namespace: "system_performance" + description: "track tail of message queue." + bug: "305311707" + is_fixed_read_only: true } flag { - name: "battery_service_support_current_adb_command" - namespace: "backstage_power" - description: "Whether or not BatteryService supports adb commands for Current values." - is_fixed_read_only: true - bug: "315037695" + name: "network_time_uses_shared_memory" + namespace: "system_performance" + description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" + bug: "361329788" + is_exported: true } flag { - name: "strict_mode_restricted_network" - namespace: "backstage_power" - description: "Guards StrictMode APIs for detecting restricted network access." - bug: "317250784" + name: "ordered_broadcast_multiple_permissions" + is_exported: true + namespace: "bluetooth" + description: "Guards the Context.sendOrderedBroadcastMultiplePermissions API" + bug: "345802719" } flag { - name: "binder_frozen_state_change_callback" + name: "remove_app_profiler_pss_collection" is_exported: true - namespace: "system_performance" - description: "Guards the frozen state change callback API." - bug: "361157077" + namespace: "backstage_power" + description: "Replaces background PSS collection in AppProfiler with RSS" + bug: "297542292" } flag { - name: "message_queue_tail_tracking" - namespace: "system_performance" - description: "track tail of message queue." - bug: "305311707" - is_fixed_read_only: true + name: "security_state_service" + is_exported: true + namespace: "dynamic_spl" + description: "Guards the Security State API." + bug: "302189431" } flag { - name: "battery_part_status_api" + name: "state_of_health_public" is_exported: true - namespace: "phoenix" - description: "Feature flag for adding Health HAL v3 APIs." - is_fixed_read_only: true - bug: "309792384" + namespace: "system_sw_battery" + description: "Feature flag for making state_of_health a public api." + bug: "288842045" } flag { @@ -187,12 +228,10 @@ flag { } flag { - namespace: "system_performance" - name: "telemetry_apis_framework_initialization" - is_exported: true - description: "Control framework initialization APIs of telemetry APIs feature." - is_fixed_read_only: true - bug: "324241334" + name: "strict_mode_restricted_network" + namespace: "backstage_power" + description: "Guards StrictMode APIs for detecting restricted network access." + bug: "317250784" } flag { @@ -203,48 +242,12 @@ flag { } flag { - name: "allow_consentless_bugreport_delegated_consent" - namespace: "crumpet" - description: "Allow privileged apps to call bugreport generation without enforcing user consent and delegate it to the calling app instead" - bug: "324046728" -} - -flag { - name: "get_private_space_settings" - namespace: "profile_experiences" - description: "Guards a new Private Profile API in LauncherApps" - bug: "346294653" - is_exported: true -} - -flag { - name: "mainline_vcn_platform_api" - namespace: "vcn" - description: "Expose platform APIs to mainline VCN" + namespace: "system_performance" + name: "telemetry_apis_framework_initialization" is_exported: true - bug: "366598445" -} - -flag { - name: "network_time_uses_shared_memory" - namespace: "system_performance" - description: "SystemClock.currentNetworkTimeMillis() reads network time offset from shared memory" - bug: "361329788" - is_exported: true -} - -flag { - name: "enable_angle_allow_list" - namespace: "gpu" - description: "Whether to read from angle allowlist to determine if app should use ANGLE" - is_fixed_read_only: true - bug: "370845648" + description: "Control framework initialization APIs of telemetry APIs feature." + is_fixed_read_only: true + bug: "324241334" } -flag { - name: "api_for_backported_fixes" - namespace: "media_reliability" - description: "Public API app developers use to check if a known issue is fixed on a device." - bug: "308461809" - is_exported: true -} +# keep-sorted end diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 1d8fcec8cf31..e59501f2742a 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -232,6 +232,13 @@ flag { } flag { + name: "sync_on_op_noted_api" + namespace: "permissions" + description: "New setOnOpNotedCallback API to allow subscribing to only sync ops." + bug: "372910217" +} + +flag { name: "wallet_role_icon_property_enabled" is_exported: true namespace: "wallet_integration" diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index 37a91e720aad..b384b66bf680 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -58,7 +58,6 @@ oneway interface INotificationListener void onSuggestedReplySent(String key, in CharSequence reply, int source); void onActionClicked(String key, in Notification.Action action, int source); void onNotificationClicked(String key); - // @deprecated changing allowed adjustments is no longer supported. void onAllowedAdjustmentsChanged(); void onNotificationFeedbackReceived(String key, in NotificationRankingUpdate update, in Bundle feedback); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index 7b7058e83acc..091b25ab77ce 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -319,10 +319,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS * their notifications the assistant can modify. * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what * {@link Adjustment adjustments} you are currently allowed to make.</p> - * - * @deprecated changing allowed adjustments is no longer supported. */ - @Deprecated public void onAllowedAdjustmentsChanged() { } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 5ac0c50a312e..e8ef9d65a2b4 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -1671,14 +1671,22 @@ public class PhoneStateListener { } /** @hide */ - public final void onCallBackModeStarted( - @TelephonyManager.EmergencyCallbackModeType int type) { + public final void onCallbackModeStarted( + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis, + int subId) { // not support. Can't override. Use TelephonyCallback. } /** @hide */ - public final void onCallBackModeStopped(@EmergencyCallbackModeType int type, - @EmergencyCallbackModeStopReason int reason) { + public final void onCallbackModeRestarted( + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis, + int subId) { + // not support. Can't override. Use TelephonyCallback. + } + + /** @hide */ + public final void onCallbackModeStopped(@EmergencyCallbackModeType int type, + @EmergencyCallbackModeStopReason int reason, int subId) { // not support. Can't override. Use TelephonyCallback. } diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index c360e64c8c1a..14d5800e4db7 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -41,6 +41,7 @@ import dalvik.system.VMRuntime; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; +import java.time.Duration; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -619,16 +620,20 @@ public class TelephonyCallback { /** - * Event for changes to the Emergency callback mode + * Event for changes to the emergency callback mode * * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} * - * @see EmergencyCallbackModeListener#onCallbackModeStarted(int) - * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int) + * @see EmergencyCallbackModeListener#onCallbackModeStarted(int, Duration, int) + * @see EmergencyCallbackModeListener#onCallbackModeRestarted(int, Duration, int) + * @see EmergencyCallbackModeListener#onCallbackModeStopped(int, int, int) * * @hide */ + + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi public static final int EVENT_EMERGENCY_CALLBACK_MODE_CHANGED = 40; /** @@ -1671,39 +1676,64 @@ public class TelephonyCallback { } /** - * Interface for emergency callback mode listener. + * Interface for the emergency callback mode listener. * * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public interface EmergencyCallbackModeListener { /** - * Indicates that Callback Mode has been started. + * Indicates that emergency callback mode has been started. * <p> - * This method will be called when an emergency sms/emergency call is sent - * and the callback mode is supported by the carrier. - * If an emergency SMS is transmitted during callback mode for SMS, this API will be called - * once again with TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS. + * This method will be called when an emergency SMS or emergency call is ended and + * the emergency callback mode is supported by the carrier. + * If the emergency callback mode was started for an emergency call and an emergency SMS is + * transmitted during callback mode for SMS then this API will be called once again with + * TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS. * - * @param type for callback mode entry + * @param type for the emergency callback mode entry * See {@link TelephonyManager.EmergencyCallbackModeType}. * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS + * + * @param timerDuration is the time remaining in the emergency callback mode. + * @param subId The subscription ID used to start the emergency callback mode. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type); + void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type, + @NonNull Duration timerDuration, int subId); /** - * Indicates that Callback Mode has been stopped. + * Indicates that emergency callback mode has been re-started. * <p> - * This method will be called when the callback mode timer expires or when - * a normal call/SMS is sent + * This method will be called when an emergency SMS or emergency call is ended + * in the emergency callback mode. + * This is used to restart the emergency callback mode when it is already in progress. * - * @param type for callback mode entry + * @param type for the emergency callback mode entry + * See {@link TelephonyManager.EmergencyCallbackModeType}. * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS * - * @param reason for changing callback mode + * @param timerDuration is the time remaining in the emergency callback mode. + * @param subId The subscription ID used to restart the emergency callback mode. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type, + @NonNull Duration timerDuration, int subId); + + /** + * Indicates that emergency callback mode has been stopped. + * <p> + * This method will be called when the emergency callback mode timer expires or when + * a normal call/SMS is sent + * + * @param type for the emergency callback mode entry + * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_CALL + * @see TelephonyManager#EMERGENCY_CALLBACK_MODE_SMS * + * @param reason for changing emergency callback mode * @see TelephonyManager#STOP_REASON_UNKNOWN * @see TelephonyManager#STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED * @see TelephonyManager#STOP_REASON_NORMAL_SMS_SENT @@ -1711,10 +1741,12 @@ public class TelephonyCallback { * @see TelephonyManager#STOP_REASON_EMERGENCY_SMS_SENT * @see TelephonyManager#STOP_REASON_TIMER_EXPIRED * @see TelephonyManager#STOP_REASON_USER_ACTION + * + * @param subId is the current subscription used the emergency callback mode. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, - @TelephonyManager.EmergencyCallbackModeStopReason int reason); + void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, + @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId); } /** @@ -2132,18 +2164,43 @@ public class TelephonyCallback { mediaQualityStatus))); } - public void onCallBackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type) { + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onCallbackModeStarted(@TelephonyManager.EmergencyCallbackModeType int type, + long durationMillis, int subId) { + if (!Flags.emergencyCallbackModeNotification()) return; + EmergencyCallbackModeListener listener = (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get(); Log.d(LOG_TAG, "onCallBackModeStarted:type=" + type + ", listener=" + listener); if (listener == null) return; + final Duration timerDuration = Duration.ofMillis(durationMillis); Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onCallBackModeStarted(type))); + () -> mExecutor.execute(() -> listener.onCallbackModeStarted(type, + timerDuration, subId))); } - public void onCallBackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, - @TelephonyManager.EmergencyCallbackModeStopReason int reason) { + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onCallbackModeRestarted(@TelephonyManager.EmergencyCallbackModeType int type, + long durationMillis, int subId) { + if (!Flags.emergencyCallbackModeNotification()) return; + + EmergencyCallbackModeListener listener = + (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get(); + Log.d(LOG_TAG, "onCallbackModeRestarted:type=" + type + ", listener=" + listener); + if (listener == null) return; + + final Duration timerDuration = Duration.ofMillis(durationMillis); + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> listener.onCallbackModeRestarted(type, + timerDuration, subId))); + } + + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void onCallbackModeStopped(@TelephonyManager.EmergencyCallbackModeType int type, + @TelephonyManager.EmergencyCallbackModeStopReason int reason, int subId) { + if (!Flags.emergencyCallbackModeNotification()) return; + EmergencyCallbackModeListener listener = (EmergencyCallbackModeListener) mTelephonyCallbackWeakRef.get(); Log.d(LOG_TAG, "onCallBackModeStopped:type=" + type @@ -2151,7 +2208,8 @@ public class TelephonyCallback { if (listener == null) return; Binder.withCleanCallingIdentity( - () -> mExecutor.execute(() -> listener.onCallBackModeStopped(type, reason))); + () -> mExecutor.execute(() -> listener.onCallbackModeStopped(type, reason, + subId))); } public void onCarrierRoamingNtnModeChanged(boolean active) { diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 10f03c15310c..3c7e924f07df 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -115,7 +115,6 @@ public class TelephonyRegistryManager { ICarrierConfigChangeListener> mCarrierConfigChangeListenerMap = new ConcurrentHashMap<>(); - /** @hide **/ public TelephonyRegistryManager(@NonNull Context context) { mContext = context; @@ -1721,13 +1720,36 @@ public class TelephonyRegistryManager { * @param subId Sender subscription ID. * @param type for callback mode entry. * See {@link TelephonyManager.EmergencyCallbackModeType}. + * @param durationMillis is the number of milliseconds remaining in the emergency callback + * mode. + * @hide + */ + public void notifyCallbackModeStarted(int phoneId, int subId, + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis) { + try { + Log.d(TAG, "notifyCallbackModeStarted:type=" + type); + sRegistry.notifyCallbackModeStarted(phoneId, subId, type, durationMillis); + } catch (RemoteException ex) { + // system process is dead + throw ex.rethrowFromSystemServer(); + } + } + + /** + * Notify Callback Mode has been restarted. + * @param phoneId Sender phone ID. + * @param subId Sender subscription ID. + * @param type for callback mode entry. + * See {@link TelephonyManager.EmergencyCallbackModeType}. + * @param durationMillis is the number of milliseconds remaining in the emergency callback + * mode. * @hide */ - public void notifyCallBackModeStarted(int phoneId, int subId, - @TelephonyManager.EmergencyCallbackModeType int type) { + public void notifyCallbackModeRestarted(int phoneId, int subId, + @TelephonyManager.EmergencyCallbackModeType int type, long durationMillis) { try { - Log.d(TAG, "notifyCallBackModeStarted:type=" + type); - sRegistry.notifyCallbackModeStarted(phoneId, subId, type); + Log.d(TAG, "notifyCallbackModeRestarted:type=" + type); + sRegistry.notifyCallbackModeRestarted(phoneId, subId, type, durationMillis); } catch (RemoteException ex) { // system process is dead throw ex.rethrowFromSystemServer(); diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 8a8022c0206a..e940e55bd38b 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS; import static android.hardware.flags.Flags.FLAG_OVERLAYPROPERTIES_CLASS_API; import static com.android.server.display.feature.flags.Flags.FLAG_HIGHEST_HDR_SDR_RATIO_API; +import static com.android.server.display.feature.flags.Flags.FLAG_ENABLE_HAS_ARR_SUPPORT; import android.Manifest; import android.annotation.FlaggedApi; @@ -1266,6 +1267,18 @@ public final class Display { } /** + * Returns whether display supports adaptive refresh rate or not. + */ + // TODO(b/372526856) Add a link to the documentation for ARR. + @FlaggedApi(FLAG_ENABLE_HAS_ARR_SUPPORT) + public boolean hasArrSupport() { + synchronized (mLock) { + updateDisplayInfoLocked(); + return mDisplayInfo.hasArrSupport; + } + } + + /** * <p> Returns true if the connected display can be switched into a mode with minimal * post processing. </p> * diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index cac3e3c25098..26fce904eb5e 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -198,6 +198,12 @@ public final class DisplayInfo implements Parcelable { public float renderFrameRate; /** + * If {@code true} this Display supports adaptive refresh rates. + * // TODO(b/372526856) Add a link to the documentation for ARR. + */ + public boolean hasArrSupport; + + /** * The default display mode. */ public int defaultModeId; @@ -436,6 +442,7 @@ public final class DisplayInfo implements Parcelable { && rotation == other.rotation && modeId == other.modeId && renderFrameRate == other.renderFrameRate + && hasArrSupport == other.hasArrSupport && defaultModeId == other.defaultModeId && userPreferredModeId == other.userPreferredModeId && Arrays.equals(supportedModes, other.supportedModes) @@ -497,6 +504,7 @@ public final class DisplayInfo implements Parcelable { rotation = other.rotation; modeId = other.modeId; renderFrameRate = other.renderFrameRate; + hasArrSupport = other.hasArrSupport; defaultModeId = other.defaultModeId; userPreferredModeId = other.userPreferredModeId; supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); @@ -553,6 +561,7 @@ public final class DisplayInfo implements Parcelable { rotation = source.readInt(); modeId = source.readInt(); renderFrameRate = source.readFloat(); + hasArrSupport = source.readBoolean(); defaultModeId = source.readInt(); userPreferredModeId = source.readInt(); int nModes = source.readInt(); @@ -626,6 +635,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(rotation); dest.writeInt(modeId); dest.writeFloat(renderFrameRate); + dest.writeBoolean(hasArrSupport); dest.writeInt(defaultModeId); dest.writeInt(userPreferredModeId); dest.writeInt(supportedModes.length); @@ -871,6 +881,8 @@ public final class DisplayInfo implements Parcelable { sb.append(modeId); sb.append(", renderFrameRate "); sb.append(renderFrameRate); + sb.append(", hasArrSupport "); + sb.append(hasArrSupport); sb.append(", defaultMode "); sb.append(defaultModeId); sb.append(", userPreferredModeId "); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index d08873c56e6a..59c66532fe0b 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -22,6 +22,7 @@ import static android.view.InsetsControllerProto.CONTROL; import static android.view.InsetsControllerProto.STATE; import static android.view.InsetsSource.ID_IME; import static android.view.InsetsSource.ID_IME_CAPTION_BAR; +import static android.view.ViewProtoLogGroups.IME_INSETS_CONTROLLER; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.all; @@ -69,6 +70,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.ImeTracing; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.function.TriFunction; import java.io.PrintWriter; @@ -1920,6 +1922,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @InsetsType int requestedVisibleTypes = (mRequestedVisibleTypes & ~mask) | (visibleTypes & mask); if (mRequestedVisibleTypes != requestedVisibleTypes) { + ProtoLog.d(IME_INSETS_CONTROLLER, "Setting requestedVisibleTypes to %d (was %d)", + requestedVisibleTypes, mRequestedVisibleTypes); mRequestedVisibleTypes = requestedVisibleTypes; } } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 19e244aa5981..e6de478e3d3d 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -1810,6 +1810,7 @@ public final class SurfaceControl implements Parcelable { public DisplayMode[] supportedDisplayModes; public int activeDisplayModeId; public float renderFrameRate; + public boolean hasArrSupport; public int[] supportedColorModes; public int activeColorMode; @@ -1827,6 +1828,7 @@ public final class SurfaceControl implements Parcelable { + "supportedDisplayModes=" + Arrays.toString(supportedDisplayModes) + ", activeDisplayModeId=" + activeDisplayModeId + ", renderFrameRate=" + renderFrameRate + + ", hasArrSupport=" + hasArrSupport + ", supportedColorModes=" + Arrays.toString(supportedColorModes) + ", activeColorMode=" + activeColorMode + ", hdrCapabilities=" + hdrCapabilities @@ -1846,13 +1848,14 @@ public final class SurfaceControl implements Parcelable { && Arrays.equals(supportedColorModes, that.supportedColorModes) && activeColorMode == that.activeColorMode && Objects.equals(hdrCapabilities, that.hdrCapabilities) - && preferredBootDisplayMode == that.preferredBootDisplayMode; + && preferredBootDisplayMode == that.preferredBootDisplayMode + && hasArrSupport == that.hasArrSupport; } @Override public int hashCode() { return Objects.hash(Arrays.hashCode(supportedDisplayModes), activeDisplayModeId, - renderFrameRate, activeColorMode, hdrCapabilities); + renderFrameRate, activeColorMode, hdrCapabilities, hasArrSupport); } } diff --git a/core/java/android/view/ViewProtoLogGroups.java b/core/java/android/view/ViewProtoLogGroups.java new file mode 100644 index 000000000000..099f76189a50 --- /dev/null +++ b/core/java/android/view/ViewProtoLogGroups.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.view; + +import android.annotation.NonNull; +import android.view.inputmethod.Flags; + +import com.android.internal.protolog.ProtoLogGroup; + +import java.util.UUID; + +/** + * Defines logging groups for ProtoLog. + * + * This file is used by the ProtoLogTool to generate optimized logging code. All of its dependencies + * must be included in services.core.wm.protologgroups build target. + * + * @hide + */ +final class ViewProtoLogGroups { + final static ProtoLogGroup IME_INSETS_CONTROLLER = new ProtoLogGroup( + "IME_INSETS_CONTROLLER", "InsetsController", Flags.refactorInsetsController()); + + final static ProtoLogGroup[] ALL_GROUPS = { + IME_INSETS_CONTROLLER + }; +} + diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5afc7b2c5cef..182ed1ebad59 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -279,11 +279,13 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneFallbackEventHandler; +import com.android.internal.protolog.ProtoLog; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.BaseSurfaceHolder; import com.android.internal.view.RootViewSurfaceTaker; import com.android.internal.view.SurfaceCallbackHelper; import com.android.modules.expresslog.Counter; +import com.android.os.coregraphics.HwuiStatsLog; import libcore.io.IoUtils; @@ -1228,6 +1230,8 @@ public final class ViewRootImpl implements ViewParent, // The latest input event from the gesture that was used to resolve the pointer icon. private MotionEvent mPointerIconEvent = null; + private @ActivityInfo.ColorMode int mCurrentColorMode = ActivityInfo.COLOR_MODE_DEFAULT; + private long mColorModeLastSetMillis = -1; public ViewRootImpl(Context context, Display display) { this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout()); @@ -1279,6 +1283,8 @@ public final class ViewRootImpl implements ViewParent, mIsStylusPointerIconEnabled = InputSettings.isStylusPointerIconEnabled(mContext); + initializeProtoLogInProcess(); + String processorOverrideName = context.getResources().getString( R.string.config_inputEventCompatProcessorOverrideClassName); if (processorOverrideName.isEmpty()) { @@ -2646,6 +2652,7 @@ public final class ViewRootImpl implements ViewParent, mFirstFramePresentedTimeNs = -1; } } + logColorMode(mCurrentColorMode, true); } @@ -6341,6 +6348,7 @@ public final class ViewRootImpl implements ViewParent, if (mAttachInfo.mThreadedRenderer == null) { return; } + boolean isHdr = colorMode == ActivityInfo.COLOR_MODE_HDR || colorMode == ActivityInfo.COLOR_MODE_HDR10; if (isHdr && !mDisplay.isHdrSdrRatioAvailable()) { @@ -6353,6 +6361,9 @@ public final class ViewRootImpl implements ViewParent, && !getConfiguration().isScreenWideColorGamut()) { colorMode = ActivityInfo.COLOR_MODE_DEFAULT; } + + logColorMode(colorMode, false); + float automaticRatio = mAttachInfo.mThreadedRenderer.setColorMode(colorMode); if (desiredRatio == 0 || desiredRatio > automaticRatio) { desiredRatio = automaticRatio; @@ -6963,9 +6974,7 @@ public final class ViewRootImpl implements ViewParent, handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj); break; case MSG_PAUSED_FOR_SYNC_TIMEOUT: - Log.e(mTag, "Timedout waiting to unpause for sync"); - mNumPausedForSync = 0; - scheduleTraversals(); + resumeAfterSyncTimeout(); break; case MSG_CHECK_INVALIDATION_IDLE: { long delta; @@ -10005,6 +10014,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer = null; mAttachInfo.mHardwareAccelerated = false; + logColorMode(mCurrentColorMode, true); } } @@ -12768,6 +12778,15 @@ public final class ViewRootImpl implements ViewParent, activeSurfaceSyncGroup.addTransaction(t); } + /** + * Resume rendering after being paused for sync due to a timeout. + */ + private void resumeAfterSyncTimeout() { + Log.e(mTag, "Timedout waiting to unpause for sync mNumPausedForSync=" + mNumPausedForSync); + mNumPausedForSync = 0; + scheduleTraversals(); + } + @Override public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() { boolean newSyncGroup = false; @@ -12795,6 +12814,16 @@ public final class ViewRootImpl implements ViewParent, } }); newSyncGroup = true; + + // If the sync group is marked ready by a timeout, check if rendering is paused and + // if it is, resume rendering and trigger a traversal. + mActiveSurfaceSyncGroup.addSyncCompleteCallback(mExecutor, () -> { + if (mActiveSurfaceSyncGroup != null + && mActiveSurfaceSyncGroup.isComplete() && mNumPausedForSync > 0) { + mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); + resumeAfterSyncTimeout(); + } + }); } Trace.instant(Trace.TRACE_TAG_VIEW, @@ -12809,12 +12838,20 @@ public final class ViewRootImpl implements ViewParent, } } - mNumPausedForSync++; - mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); - mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, - 1000 * Build.HW_TIMEOUT_MULTIPLIER); + // The sync group can be marked ready by a timeout. This makes incrementing + // mNumPausedForSync racy. Here we check if the sync group is complete and + // if it is then we don't pause for syncing. + if (!mActiveSurfaceSyncGroup.isComplete()) { + mNumPausedForSync++; + mHandler.removeMessages(MSG_PAUSED_FOR_SYNC_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_PAUSED_FOR_SYNC_TIMEOUT, + 1000 * Build.HW_TIMEOUT_MULTIPLIER); + } else { + Log.d(mTag, "Active sync group is already completed " + + mActiveSurfaceSyncGroup.getName()); + } return mActiveSurfaceSyncGroup; - }; + } private final Executor mSimpleExecutor = Runnable::run; @@ -13346,4 +13383,36 @@ public final class ViewRootImpl implements ViewParent, mInfrequentUpdateCount = 0; } } + + private void logColorMode(@ActivityInfo.ColorMode int colorMode, boolean windowStopped) { + if (mColorModeLastSetMillis == -1 && windowStopped) { + Log.d(TAG, "Skipping stats log for color mode"); + return; + } + + long currentTimeMillis = System.currentTimeMillis(); + + if (windowStopped) { + HwuiStatsLog.write(HwuiStatsLog.HARDWARE_RENDERER_EVENT, Process.myUid(), + currentTimeMillis - mColorModeLastSetMillis, mCurrentColorMode); + mColorModeLastSetMillis = -1; + } else { + if (mColorModeLastSetMillis > 0) { + HwuiStatsLog.write(HwuiStatsLog.HARDWARE_RENDERER_EVENT, Process.myUid(), + currentTimeMillis - mColorModeLastSetMillis, mCurrentColorMode); + } + mColorModeLastSetMillis = currentTimeMillis; + } + + mCurrentColorMode = colorMode; + } + + private static boolean sProtoLogInitialized = false; + + private void initializeProtoLogInProcess() { + if (!sProtoLogInitialized) { + ProtoLog.init(ViewProtoLogGroups.ALL_GROUPS); + sProtoLogInitialized = true; + } + } } diff --git a/core/java/android/widget/RemoteCollectionItemsAdapter.java b/core/java/android/widget/RemoteCollectionItemsAdapter.java index 9b396aeea9eb..fc09f880ccd4 100644 --- a/core/java/android/widget/RemoteCollectionItemsAdapter.java +++ b/core/java/android/widget/RemoteCollectionItemsAdapter.java @@ -40,13 +40,15 @@ class RemoteCollectionItemsAdapter extends BaseAdapter { private RemoteCollectionItems mItems; private InteractionHandler mInteractionHandler; private ColorResources mColorResources; + private boolean mOnLightBackground; private SparseIntArray mLayoutIdToViewType; RemoteCollectionItemsAdapter( @NonNull RemoteCollectionItems items, @NonNull InteractionHandler interactionHandler, - @NonNull ColorResources colorResources) { + @NonNull ColorResources colorResources, + boolean onLightBackground) { // View type count can never increase after an adapter has been set on a ListView. // Additionally, decreasing it could inhibit view recycling if the count were to back and // forth between 3-2-3-2 for example. Therefore, the view type count, should be fixed for @@ -56,6 +58,7 @@ class RemoteCollectionItemsAdapter extends BaseAdapter { mItems = items; mInteractionHandler = interactionHandler; mColorResources = colorResources; + mOnLightBackground = onLightBackground; initLayoutIdToViewType(); } @@ -68,7 +71,8 @@ class RemoteCollectionItemsAdapter extends BaseAdapter { void setData( @NonNull RemoteCollectionItems items, @NonNull InteractionHandler interactionHandler, - @NonNull ColorResources colorResources) { + @NonNull ColorResources colorResources, + boolean onLightBackground) { if (mViewTypeCount < items.getViewTypeCount()) { throw new IllegalArgumentException( "RemoteCollectionItemsAdapter cannot increase view type count after creation"); @@ -77,6 +81,7 @@ class RemoteCollectionItemsAdapter extends BaseAdapter { mItems = items; mInteractionHandler = interactionHandler; mColorResources = colorResources; + mOnLightBackground = onLightBackground; initLayoutIdToViewType(); @@ -184,6 +189,7 @@ class RemoteCollectionItemsAdapter extends BaseAdapter { : new AppWidgetHostView.AdapterChildHostView(parent.getContext()); newView.setInteractionHandler(mInteractionHandler); newView.setColorResourcesNoReapply(mColorResources); + newView.setOnLightBackground(mOnLightBackground); newView.updateAppWidget(item); return newView; } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index d7b5211ad9fd..9b6311f35d17 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1248,6 +1248,7 @@ public class RemoteViews implements Parcelable, Filter { AdapterView adapterView = (AdapterView) target; Adapter adapter = adapterView.getAdapter(); + boolean onLightBackground = hasFlags(FLAG_USE_LIGHT_BACKGROUND_LAYOUT); // We can reuse the adapter if it's a RemoteCollectionItemsAdapter and the view type // count hasn't increased. Note that AbsListView allocates a fixed size array for view // recycling in setAdapter, so we must call setAdapter again if the number of view types @@ -1255,8 +1256,12 @@ public class RemoteViews implements Parcelable, Filter { if (adapter instanceof RemoteCollectionItemsAdapter && adapter.getViewTypeCount() >= items.getViewTypeCount()) { try { - ((RemoteCollectionItemsAdapter) adapter).setData( - items, params.handler, params.colorResources); + ((RemoteCollectionItemsAdapter) adapter) + .setData( + items, + params.handler, + params.colorResources, + onLightBackground); } catch (Throwable throwable) { // setData should never failed with the validation in the items builder, but if // it does, catch and rethrow. @@ -1266,8 +1271,9 @@ public class RemoteViews implements Parcelable, Filter { } try { - adapterView.setAdapter(new RemoteCollectionItemsAdapter(items, - params.handler, params.colorResources)); + adapterView.setAdapter( + new RemoteCollectionItemsAdapter( + items, params.handler, params.colorResources, onLightBackground)); } catch (Throwable throwable) { // This could throw if the AdapterView somehow doesn't accept BaseAdapter due to // a type error. diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java index d3733b71f7ee..23e572fcd577 100644 --- a/core/java/android/window/BackEvent.java +++ b/core/java/android/window/BackEvent.java @@ -16,8 +16,13 @@ package android.window; +import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; + +import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; +import android.util.TimeUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -44,18 +49,25 @@ public final class BackEvent { private final float mTouchX; private final float mTouchY; private final float mProgress; + private final long mFrameTime; @SwipeEdge private final int mSwipeEdge; /** @hide */ public static BackEvent fromBackMotionEvent(BackMotionEvent backMotionEvent) { - return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), - backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge()); + if (predictiveBackTimestampApi()) { + return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), + backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge(), + backMotionEvent.getFrameTime()); + } else { + return new BackEvent(backMotionEvent.getTouchX(), backMotionEvent.getTouchY(), + backMotionEvent.getProgress(), backMotionEvent.getSwipeEdge()); + } } /** - * Creates a new {@link BackEvent} instance. + * Creates a new {@link BackEvent} instance with the current uptime as frame time. * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. @@ -67,6 +79,26 @@ public final class BackEvent { mTouchY = touchY; mProgress = progress; mSwipeEdge = swipeEdge; + mFrameTime = System.nanoTime() / TimeUtils.NANOS_PER_MS; + } + + /** + * Creates a new {@link BackEvent} instance. + * + * @param touchX Absolute X location of the touch point of this event. + * @param touchY Absolute Y location of the touch point of this event. + * @param progress Value between 0 and 1 on how far along the back gesture is. + * @param swipeEdge Indicates which edge the swipe starts from. + * @param frameTime frame time of the back event. + */ + @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) + public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge, + long frameTime) { + mTouchX = touchX; + mTouchY = touchY; + mProgress = progress; + mSwipeEdge = swipeEdge; + mFrameTime = frameTime; } /** @@ -115,13 +147,38 @@ public final class BackEvent { return mSwipeEdge; } + /** + * Returns the frameTime of the BackEvent in milliseconds. Useful for calculating velocity. + */ + @FlaggedApi(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) + public long getFrameTime() { + return mFrameTime; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof BackEvent)) { + return false; + } + final BackEvent that = (BackEvent) other; + return mTouchX == that.mTouchX + && mTouchY == that.mTouchY + && mProgress == that.mProgress + && mSwipeEdge == that.mSwipeEdge + && mFrameTime == that.mFrameTime; + } + @Override public String toString() { return "BackEvent{" + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + ", mProgress=" + mProgress - + ", mSwipeEdge" + mSwipeEdge + + ", mSwipeEdge=" + mSwipeEdge + + ", mFrameTime=" + mFrameTime + "ms" + "}"; } } diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index 8ac68abd8d8e..a8ec4eeb039a 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -33,6 +33,7 @@ import android.view.RemoteAnimationTarget; public final class BackMotionEvent implements Parcelable { private final float mTouchX; private final float mTouchY; + private final long mFrameTime; private final float mProgress; private final boolean mTriggerBack; @@ -48,6 +49,7 @@ public final class BackMotionEvent implements Parcelable { * * @param touchX Absolute X location of the touch point of this event. * @param touchY Absolute Y location of the touch point of this event. + * @param frameTime Event time of the corresponding touch event. * @param progress Value between 0 and 1 on how far along the back gesture is. * @param triggerBack Indicates whether the back arrow is in the triggered state or not * @param swipeEdge Indicates which edge the swipe starts from. @@ -57,12 +59,14 @@ public final class BackMotionEvent implements Parcelable { public BackMotionEvent( float touchX, float touchY, + long frameTime, float progress, boolean triggerBack, @BackEvent.SwipeEdge int swipeEdge, @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; + mFrameTime = frameTime; mProgress = progress; mTriggerBack = triggerBack; mSwipeEdge = swipeEdge; @@ -76,6 +80,7 @@ public final class BackMotionEvent implements Parcelable { mTriggerBack = in.readBoolean(); mSwipeEdge = in.readInt(); mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); + mFrameTime = in.readLong(); } @NonNull @@ -104,6 +109,7 @@ public final class BackMotionEvent implements Parcelable { dest.writeBoolean(mTriggerBack); dest.writeInt(mSwipeEdge); dest.writeTypedObject(mDepartingAnimationTarget, flags); + dest.writeLong(mFrameTime); } /** @@ -148,6 +154,13 @@ public final class BackMotionEvent implements Parcelable { } /** + * Returns the frame time of the BackMotionEvent in milliseconds. + */ + public long getFrameTime() { + return mFrameTime; + } + + /** * Returns the {@link RemoteAnimationTarget} of the top departing application window, * or {@code null} if the top window should not be moved for the current type of back * destination. @@ -162,10 +175,11 @@ public final class BackMotionEvent implements Parcelable { return "BackMotionEvent{" + "mTouchX=" + mTouchX + ", mTouchY=" + mTouchY + + ", mFrameTime=" + mFrameTime + "ms" + ", mProgress=" + mProgress + ", mTriggerBack=" + mTriggerBack - + ", mSwipeEdge" + mSwipeEdge - + ", mDepartingAnimationTarget" + mDepartingAnimationTarget + + ", mSwipeEdge=" + mSwipeEdge + + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget + "}"; } } diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java index 465e11a048f0..a5be58b7b183 100644 --- a/core/java/android/window/BackProgressAnimator.java +++ b/core/java/android/window/BackProgressAnimator.java @@ -17,6 +17,7 @@ package android.window; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -66,7 +67,8 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL reset(); }; private final DynamicAnimation.OnAnimationUpdateListener mOnBackInvokedFlingUpdateListener = - (animation, progress, velocity) -> updateProgressValue(progress, velocity); + (animation, progress, velocity) -> + updateProgressValue(progress, velocity, animation.getLastFrameTime()); private void setProgress(float progress) { @@ -92,7 +94,9 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL @Override public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { - if (mBackInvokedFinishRunnable == null) updateProgressValue(value, velocity); + if (mBackInvokedFinishRunnable == null) { + updateProgressValue(value, velocity, animation.getLastFrameTime()); + } } @@ -137,7 +141,9 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL mLastBackEvent = event; mCallback = callback; mBackAnimationInProgress = true; - updateProgressValue(0, 0); + updateProgressValue(/* progress */ 0, /* velocity */ 0, + /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS); + onBackProgressed(event); } /** @@ -146,7 +152,8 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL public void reset() { if (mBackCancelledFinishRunnable != null) { // Ensure that last progress value that apps see is 0 - updateProgressValue(0, 0); + updateProgressValue(/* progress */ 0, /* velocity */ 0, + /* frameTime */ System.nanoTime() / TimeUtils.NANOS_PER_MS); invokeBackCancelledRunnable(); } else if (mBackInvokedFinishRunnable != null) { invokeBackInvokedRunnable(); @@ -236,14 +243,20 @@ public class BackProgressAnimator implements DynamicAnimation.OnAnimationUpdateL return mVelocity / SCALE_FACTOR; } - private void updateProgressValue(float progress, float velocity) { + private void updateProgressValue(float progress, float velocity, long frameTime) { mVelocity = velocity; if (mLastBackEvent == null || mCallback == null || !mBackAnimationInProgress) { return; } - mCallback.onProgressUpdate( - new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), - progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge())); + BackEvent backEvent; + if (predictiveBackTimestampApi()) { + backEvent = new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), + progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge(), frameTime); + } else { + backEvent = new BackEvent(mLastBackEvent.getTouchX(), mLastBackEvent.getTouchY(), + progress / SCALE_FACTOR, mLastBackEvent.getSwipeEdge()); + } + mCallback.onProgressUpdate(backEvent); } private void invokeBackCancelledRunnable() { diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java index 290c494b1bff..39a30253adbd 100644 --- a/core/java/android/window/BackTouchTracker.java +++ b/core/java/android/window/BackTouchTracker.java @@ -151,6 +151,7 @@ public class BackTouchTracker { return new BackMotionEvent( /* touchX = */ mInitTouchX, /* touchY = */ mInitTouchY, + /* frameTime = */ 0, /* progress = */ 0, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, @@ -235,6 +236,7 @@ public class BackTouchTracker { return new BackMotionEvent( /* touchX = */ mLatestTouchX, /* touchY = */ mLatestTouchY, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ mTriggerBack, /* swipeEdge = */ mSwipeEdge, diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 66c35e2fe837..8db1f9509757 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -17,6 +17,7 @@ package android.window; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -235,8 +236,12 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @Override public void onBackStarted(@NonNull BackEvent backEvent) { try { + long frameTime = 0; + if (predictiveBackTimestampApi()) { + frameTime = backEvent.getFrameTime(); + } mIOnBackInvokedCallback.onBackStarted( - new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), + new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { @@ -247,8 +252,12 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @Override public void onBackProgressed(@NonNull BackEvent backEvent) { try { + long frameTime = 0; + if (predictiveBackTimestampApi()) { + frameTime = backEvent.getFrameTime(); + } mIOnBackInvokedCallback.onBackProgressed( - new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), + new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, backEvent.getProgress(), false, backEvent.getSwipeEdge(), null)); } catch (RemoteException e) { diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java index 5d14698c82b3..a68bdc05e20e 100644 --- a/core/java/android/window/SurfaceSyncGroup.java +++ b/core/java/android/window/SurfaceSyncGroup.java @@ -839,6 +839,16 @@ public final class SurfaceSyncGroup { } /** + * Returns true if the SurfaceSyncGroup has completed its sync. + * @hide + */ + public boolean isComplete() { + synchronized (mLock) { + return mFinished; + } + } + + /** * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must * implement onFrameStarted when trying to sync the SurfaceView. This is to ensure the sync * knows when the frame is ready to add to the sync. diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java index 20d1b3bd12ae..a37bef80ff04 100644 --- a/core/java/android/window/TaskSnapshot.java +++ b/core/java/android/window/TaskSnapshot.java @@ -83,13 +83,16 @@ public class TaskSnapshot implements Parcelable { public static final int REFERENCE_CACHE = 1 << 1; /** This snapshot object is being persistent. */ public static final int REFERENCE_PERSIST = 1 << 2; + /** This snapshot object is being used for content suggestion. */ + public static final int REFERENCE_CONTENT_SUGGESTION = 1 << 3; @IntDef(flag = true, prefix = { "REFERENCE_" }, value = { REFERENCE_BROADCAST, REFERENCE_CACHE, - REFERENCE_PERSIST + REFERENCE_PERSIST, + REFERENCE_CONTENT_SUGGESTION }) @Retention(RetentionPolicy.SOURCE) - @interface ReferenceFlags {} + public @interface ReferenceFlags {} public TaskSnapshot(long id, long captureTime, @NonNull ComponentName topActivityComponent, HardwareBuffer snapshot, diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 8e495ec1dc40..34abf3114925 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -525,6 +525,22 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * Disables or enables activities to be started in adjacent tasks (see + * {@link FLAG_ACTIVITY_LAUNCH_ADJACENT}) for the specified root of any child tasks. This + * differs from {@link #setLaunchAdjacentFlagRoot(WindowContainerToken)} which controls the + * preferred launch-adjacent target and allows for selectively setting which root tasks can + * support launch-adjacent. + * @hide + */ + @NonNull + public WindowContainerTransaction setDisableLaunchAdjacent( + @NonNull WindowContainerToken container, boolean disabled) { + mHierarchyOps.add(HierarchyOp.createForSetDisableLaunchAdjacent(container.asBinder(), + disabled)); + return this; + } + + /** * Starts a task by id. The task is expected to already exist (eg. as a recent task). * @param taskId Id of task to start. * @param options bundle containing ActivityOptions for the task's top activity. @@ -1488,6 +1504,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION = 20; public static final int HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES = 21; public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22; + public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1556,6 +1573,8 @@ public final class WindowContainerTransaction implements Parcelable { private @InsetsType int mExcludeInsetsTypes; + private boolean mLaunchAdjacentDisabled; + public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) @@ -1644,6 +1663,15 @@ public final class WindowContainerTransaction implements Parcelable { .build(); } + /** Create a hierarchy op for disabling launch adjacent. */ + public static HierarchyOp createForSetDisableLaunchAdjacent(IBinder container, + boolean disabled) { + return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT) + .setContainer(container) + .setLaunchAdjacentDisabled(disabled) + .build(); + } + /** create a hierarchy op for deleting a task **/ public static HierarchyOp createForRemoveTask(@NonNull IBinder container) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK) @@ -1695,6 +1723,7 @@ public final class WindowContainerTransaction implements Parcelable { mReparentLeafTaskIfRelaunch = copy.mReparentLeafTaskIfRelaunch; mIsTrimmableFromRecents = copy.mIsTrimmableFromRecents; mExcludeInsetsTypes = copy.mExcludeInsetsTypes; + mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled; } protected HierarchyOp(Parcel in) { @@ -1719,6 +1748,7 @@ public final class WindowContainerTransaction implements Parcelable { mReparentLeafTaskIfRelaunch = in.readBoolean(); mIsTrimmableFromRecents = in.readBoolean(); mExcludeInsetsTypes = in.readInt(); + mLaunchAdjacentDisabled = in.readBoolean(); } public int getType() { @@ -1814,13 +1844,11 @@ public final class WindowContainerTransaction implements Parcelable { } /** Denotes whether the parents should also be included in the op. */ - @NonNull public boolean includingParents() { return mIncludingParents; } - /** Set the task to be trimmable */ - @NonNull + /** Denotes whether the task can be trimmable from recents */ public boolean isTrimmableFromRecents() { return mIsTrimmableFromRecents; } @@ -1829,6 +1857,11 @@ public final class WindowContainerTransaction implements Parcelable { return mExcludeInsetsTypes; } + /** Denotes whether launch-adjacent flag is respected from this task or its children */ + public boolean isLaunchAdjacentDisabled() { + return mLaunchAdjacentDisabled; + } + /** Gets a string representation of a hierarchy-op type. */ public static String hopToString(int type) { switch (type) { @@ -1839,6 +1872,8 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot"; case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask"; case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot"; + case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: + return "SetDisableLaunchAdjacent"; case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent"; case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut"; case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider"; @@ -1891,6 +1926,10 @@ public final class WindowContainerTransaction implements Parcelable { case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop); break; + case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: + sb.append("container=").append(mContainer).append(" disabled=") + .append(mLaunchAdjacentDisabled); + break; case HIERARCHY_OP_TYPE_START_SHORTCUT: sb.append("options=").append(mLaunchOptions) .append(" info=").append(mShortcutInfo); @@ -1971,6 +2010,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeBoolean(mReparentLeafTaskIfRelaunch); dest.writeBoolean(mIsTrimmableFromRecents); dest.writeInt(mExcludeInsetsTypes); + dest.writeBoolean(mLaunchAdjacentDisabled); } @Override @@ -2047,6 +2087,8 @@ public final class WindowContainerTransaction implements Parcelable { private @InsetsType int mExcludeInsetsTypes; + private boolean mLaunchAdjacentDisabled; + Builder(int type) { mType = type; } @@ -2153,6 +2195,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setLaunchAdjacentDisabled(boolean disabled) { + mLaunchAdjacentDisabled = disabled; + return this; + } + HierarchyOp build() { final HierarchyOp hierarchyOp = new HierarchyOp(mType); hierarchyOp.mContainer = mContainer; @@ -2179,6 +2226,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch; hierarchyOp.mIsTrimmableFromRecents = mIsTrimmableFromRecents; hierarchyOp.mExcludeInsetsTypes = mExcludeInsetsTypes; + hierarchyOp.mLaunchAdjacentDisabled = mLaunchAdjacentDisabled; return hierarchyOp; } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 44a374fb7c20..c9d458f22463 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -17,6 +17,7 @@ package android.window; import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver; +import static com.android.window.flags.Flags.predictiveBackTimestampApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -563,7 +564,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } OnBackAnimationCallback animationCallback = getBackAnimationCallback(); if (animationCallback != null - && !(callback instanceof ImeBackAnimationController)) { + && !(callback instanceof ImeBackAnimationController) + && !predictiveBackTimestampApi()) { mProgressAnimator.onBackInvoked(() -> { if (mIsSystemCallback) { mSystemNavigationObserverCallbackRunnable.run(); diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig index f739622a1b92..235ba3a41427 100644 --- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig +++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig @@ -37,6 +37,13 @@ flag { } flag { + name: "move_to_external_display_shortcut" + namespace: "large_screen_experiences_app_compat" + description: "Whether the move to external display shortcut in overview is available" + bug: "372872848" +} + +flag { name: "app_compat_properties_api" is_exported: true namespace: "large_screen_experiences_app_compat" diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 155494fb3b25..18129530978f 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -239,6 +239,13 @@ flag { } flag { + name: "enable_desktop_windowing_enter_transitions" + namespace: "lse_desktop_experience" + description: "Enables enter desktop windowing transition & motion polish changes" + bug: "369763947" +} + +flag { name: "enable_desktop_windowing_exit_transitions" namespace: "lse_desktop_experience" description: "Enables exit desktop windowing transition & motion polish changes" diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 622f8c817d99..0d235ffad9b5 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -70,17 +70,6 @@ flag { } flag { - name: "common_surface_animator" - namespace: "windowing_frontend" - description: "A reusable surface animator for default transition" - bug: "326331384" - is_fixed_read_only: true - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "reduce_keyguard_transitions" namespace: "windowing_frontend" description: "Avoid setting keyguard transitions ready unless there are no other changes" @@ -325,3 +314,11 @@ flag { is_fixed_read_only: true bug: "362938401" } + +flag { + name: "predictive_back_timestamp_api" + namespace: "systemui" + description: "expose timestamp in BackEvent (API extension)" + is_fixed_read_only: true + bug: "362938401" +} diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 18c8eb4ec46b..de7ad346a7cd 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -1195,7 +1195,12 @@ public class ResolverListAdapter extends BaseAdapter { @Nullable protected Drawable loadIconFromResource(Resources res, int resId) { - return res.getDrawableForDensity(resId, mIconDpi); + try { + return res.getDrawableForDensity(resId, mIconDpi); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource not found", e); + return null; + } } } diff --git a/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java b/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java index d4fe7c8d7f36..7a4c1a00a41f 100644 --- a/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java +++ b/core/java/com/android/internal/dynamicanimation/animation/DynamicAnimation.java @@ -762,6 +762,10 @@ public abstract class DynamicAnimation<T extends DynamicAnimation<T>> return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance(); } + public long getLastFrameTime() { + return mLastFrameTime; + } + /****************Sub class animations**************/ /** * Returns the acceleration at the given value with the given velocity. diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index f3dc896a765e..a037cb421b0c 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -218,6 +218,22 @@ public class PerfettoProtoLogImpl extends IProtoLogClient.Stub implements IProto // NOTE: Registering that datasource is an async operation, so there may be no data traced // for some messages logged right after the construction of this class. mDataSource.register(params); + + if (viewerConfigInputStreamProvider == null && viewerConfigFilePath != null) { + viewerConfigInputStreamProvider = new ViewerConfigInputStreamProvider() { + @NonNull + @Override + public ProtoInputStream getInputStream() { + try { + return new ProtoInputStream(new FileInputStream(viewerConfigFilePath)); + } catch (FileNotFoundException e) { + throw new RuntimeException( + "Failed to load viewer config file " + viewerConfigFilePath, e); + } + } + }; + } + this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; this.mViewerConfigReader = viewerConfigReader; this.mCacheUpdater = cacheUpdater; diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java index 0a80e006d5bc..dd6c879f1135 100644 --- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java +++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java @@ -39,7 +39,7 @@ public class ProtoLogViewerConfigReader { * or the viewer config is not loaded into memory. */ @Nullable - public synchronized String getViewerString(long messageHash) { + public String getViewerString(long messageHash) { return mLogMessageMap.get(messageHash); } diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index f177e1473b6a..81b885aa626b 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -78,8 +78,9 @@ oneway interface IPhoneStateListener { void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType); void onLinkCapacityEstimateChanged(in List<LinkCapacityEstimate> linkCapacityEstimateList); void onMediaQualityStatusChanged(in MediaQualityStatus mediaQualityStatus); - void onCallBackModeStarted(int type); - void onCallBackModeStopped(int type, int reason); + void onCallbackModeStarted(int type, long durationMillis, int subId); + void onCallbackModeRestarted(int type, long durationMillis, int subId); + void onCallbackModeStopped(int type, int reason, int subId); void onSimultaneousCallingStateChanged(in int[] subIds); void onCarrierRoamingNtnModeChanged(in boolean active); void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible); diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index e500a37abb53..f836cf2b9d87 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -118,7 +118,8 @@ interface ITelephonyRegistry { void removeCarrierConfigChangeListener(ICarrierConfigChangeListener listener, String pkg); void notifyCarrierConfigChanged(int phoneId, int subId, int carrierId, int specificCarrierId); - void notifyCallbackModeStarted(int phoneId, int subId, int type); + void notifyCallbackModeStarted(int phoneId, int subId, int type, long durationMillis); + void notifyCallbackModeRestarted(int phoneId, int subId, int type, long durationMillis); void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason); void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active); void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible); diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java index 98e6e8505534..adcc0f64b598 100644 --- a/core/java/com/android/internal/widget/NotificationRowIconView.java +++ b/core/java/com/android/internal/widget/NotificationRowIconView.java @@ -39,17 +39,24 @@ import com.android.internal.R; /** * An image view that holds the icon displayed at the start of a notification row. + * This can generally either display the "small icon" of a notification set via + * {@link this#setImageIcon(Icon)}, or an app icon controlled and fetched by the provider set + * through {@link this#setIconProvider(NotificationIconProvider)}. */ @RemoteViews.RemoteView public class NotificationRowIconView extends CachingIconView { + private NotificationIconProvider mIconProvider; + private boolean mApplyCircularCrop = false; private boolean mShouldShowAppIcon = false; + private Drawable mAppIcon = null; - // Padding and background set on the view prior to being changed by setShouldShowAppIcon(true), - // to be restored if shouldShowAppIcon becomes false again. + // Padding, background and colors set on the view prior to being overridden when showing the app + // icon, to be restored if we're showing the small icon again. private Rect mOriginalPadding = null; private Drawable mOriginalBackground = null; - + private int mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID; + private int mOriginalIconColor = ColoredIconHelper.COLOR_INVALID; public NotificationRowIconView(Context context) { super(context); @@ -81,6 +88,71 @@ public class NotificationRowIconView extends CachingIconView { super.onFinishInflate(); } + /** + * Sets the icon provider for this view. This is used to determine whether we should show the + * app icon instead of the small icon, and to fetch the app icon if needed. + */ + public void setIconProvider(NotificationIconProvider iconProvider) { + mIconProvider = iconProvider; + } + + private Drawable loadAppIcon() { + if (mIconProvider != null && mIconProvider.shouldShowAppIcon()) { + return mIconProvider.getAppIcon(); + } + return null; + } + + @RemotableViewMethod(asyncImpl = "setImageIconAsync") + @Override + public void setImageIcon(Icon icon) { + if (Flags.notificationsRedesignAppIcons()) { + if (mAppIcon != null) { + // We already know that we should be using the app icon, and we already loaded it. + // We assume that cannot change throughout the lifetime of a notification, so + // there's nothing to do here. + return; + } + mAppIcon = loadAppIcon(); + if (mAppIcon != null) { + setImageDrawable(mAppIcon); + adjustViewForAppIcon(); + } else { + super.setImageIcon(icon); + restoreViewForSmallIcon(); + } + return; + } + super.setImageIcon(icon); + } + + @RemotableViewMethod + @Override + public Runnable setImageIconAsync(Icon icon) { + if (Flags.notificationsRedesignAppIcons()) { + if (mAppIcon != null) { + // We already know that we should be using the app icon, and we already loaded it. + // We assume that cannot change throughout the lifetime of a notification, so + // there's nothing to do here. + return () -> { + }; + } + mAppIcon = loadAppIcon(); + if (mAppIcon != null) { + return () -> { + setImageDrawable(mAppIcon); + adjustViewForAppIcon(); + }; + } else { + return () -> { + super.setImageIcon(icon); + restoreViewForSmallIcon(); + }; + } + } + return super.setImageIconAsync(icon); + } + /** Whether the icon represents the app icon (instead of the small icon). */ @RemotableViewMethod public void setShouldShowAppIcon(boolean shouldShowAppIcon) { @@ -91,35 +163,122 @@ public class NotificationRowIconView extends CachingIconView { mShouldShowAppIcon = shouldShowAppIcon; if (mShouldShowAppIcon) { - if (mOriginalPadding == null && mOriginalBackground == null) { - mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(), - getPaddingRight(), getPaddingBottom()); - mOriginalBackground = getBackground(); - } - - setPadding(0, 0, 0, 0); - - // Make the background white in case the icon itself doesn't have one. - ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE, - PorterDuff.Mode.SRC_ATOP); - - if (mOriginalBackground == null) { - setBackground(getContext().getDrawable(R.drawable.notification_icon_circle)); - } - getBackground().mutate().setColorFilter(colorFilter); + adjustViewForAppIcon(); } else { // Restore original padding and background if needed - if (mOriginalPadding != null) { - setPadding(mOriginalPadding.left, mOriginalPadding.top, mOriginalPadding.right, - mOriginalPadding.bottom); - mOriginalPadding = null; - } - setBackground(mOriginalBackground); - mOriginalBackground = null; + restoreViewForSmallIcon(); } } } + /** + * Override padding and background from the view to display the app icon. + */ + private void adjustViewForAppIcon() { + removePadding(); + + if (Flags.notificationsUseAppIconInRow()) { + addWhiteBackground(); + } else { + // No need to set the background for notification redesign, since the icon + // factory already does that for us. + removeBackground(); + } + } + + /** + * Restore padding and background overridden by {@link this#adjustViewForAppIcon}. + * Does nothing if they were not overridden. + */ + private void restoreViewForSmallIcon() { + restorePadding(); + restoreBackground(); + restoreColors(); + } + + private void removePadding() { + if (mOriginalPadding == null) { + mOriginalPadding = new Rect(getPaddingLeft(), getPaddingTop(), + getPaddingRight(), getPaddingBottom()); + } + setPadding(0, 0, 0, 0); + } + + private void restorePadding() { + if (mOriginalPadding != null) { + setPadding(mOriginalPadding.left, mOriginalPadding.top, + mOriginalPadding.right, + mOriginalPadding.bottom); + mOriginalPadding = null; + } + } + + private void removeBackground() { + if (mOriginalBackground == null) { + mOriginalBackground = getBackground(); + } + + setBackground(null); + } + + private void addWhiteBackground() { + if (mOriginalBackground == null) { + mOriginalBackground = getBackground(); + } + + // Make the background white in case the icon itself doesn't have one. + ColorFilter colorFilter = new PorterDuffColorFilter(Color.WHITE, + PorterDuff.Mode.SRC_ATOP); + + if (mOriginalBackground == null) { + setBackground(getContext().getDrawable(R.drawable.notification_icon_circle)); + } + getBackground().mutate().setColorFilter(colorFilter); + } + + private void restoreBackground() { + // NOTE: This will not work if the original background was null, but that's better than + // accidentally clearing the background. We expect that there's generally going to be one + // anyway unless we manually clear it. + if (mOriginalBackground != null) { + setBackground(mOriginalBackground); + mOriginalBackground = null; + } + } + + private void restoreColors() { + if (mOriginalBackgroundColor != ColoredIconHelper.COLOR_INVALID) { + super.setBackgroundColor(mOriginalBackgroundColor); + mOriginalBackgroundColor = ColoredIconHelper.COLOR_INVALID; + } + if (mOriginalIconColor != ColoredIconHelper.COLOR_INVALID) { + super.setOriginalIconColor(mOriginalIconColor); + mOriginalIconColor = ColoredIconHelper.COLOR_INVALID; + } + } + + @RemotableViewMethod + @Override + public void setBackgroundColor(int color) { + // Ignore color overrides if we're showing the app icon. + if (mAppIcon == null) { + super.setBackgroundColor(color); + } else { + mOriginalBackgroundColor = color; + } + } + + @RemotableViewMethod + @Override + public void setOriginalIconColor(int color) { + // Ignore color overrides if we're showing the app icon. + if (mAppIcon == null) { + super.setOriginalIconColor(color); + } else { + mOriginalIconColor = color; + } + } + @Nullable @Override Drawable loadSizeRestrictedIcon(@Nullable Icon icon) { @@ -197,4 +356,17 @@ public class NotificationRowIconView extends CachingIconView { return bitmap; } + + /** + * A provider that allows this view to verify whether it should use the app icon instead of the + * icon provided to it via setImageIcon, as well as actually fetching the app icon. It should + * primarily be called on the background thread. + */ + public interface NotificationIconProvider { + /** Whether this notification should use the app icon instead of the small icon. */ + boolean shouldShowAppIcon(); + + /** Get the app icon for this notification. */ + Drawable getAppIcon(); + } } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 9bccf5af7096..8eaa7aa99a2d 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -747,16 +747,12 @@ android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint strea indexMax)); } -static jint -android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, - jobject thiz, - jint stream, - jint index, - jint device) -{ +static jint android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream, + jint index, jboolean muted, + jint device) { return check_AudioSystem_Command( AudioSystem::setStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), index, - static_cast<audio_devices_t>(device))); + muted, static_cast<audio_devices_t>(device))); } static jint @@ -773,13 +769,9 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, return index; } -static jint -android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, - jobject thiz, - jobject jaa, - jint index, - jint device) -{ +static jint android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, jobject thiz, + jobject jaa, jint index, + jboolean muted, jint device) { // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); @@ -787,7 +779,7 @@ android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, return jStatus; } return check_AudioSystem_Command( - AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, + AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, muted, static_cast<audio_devices_t>(device))); } @@ -3448,182 +3440,179 @@ static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env, #define MAKE_AUDIO_SYSTEM_METHOD(x) \ MAKE_JNI_NATIVE_METHOD_AUTOSIG(#x, android_media_AudioSystem_##x) -static const JNINativeMethod gMethods[] = - {MAKE_AUDIO_SYSTEM_METHOD(setParameters), - MAKE_AUDIO_SYSTEM_METHOD(getParameters), - MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone), - MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted), - MAKE_AUDIO_SYSTEM_METHOD(isStreamActive), - MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely), - MAKE_AUDIO_SYSTEM_METHOD(isSourceActive), - MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId), - MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId), - MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId), - MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", - android_media_AudioSystem_setDeviceConnectionState), - MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState), - MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange), - MAKE_AUDIO_SYSTEM_METHOD(setPhoneState), - MAKE_AUDIO_SYSTEM_METHOD(setForceUse), - MAKE_AUDIO_SYSTEM_METHOD(getForceUse), - MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled), - MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume), - MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex), - MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex), - MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes", - "(Landroid/media/AudioAttributes;II)I", - android_media_AudioSystem_setVolumeIndexForAttributes), - MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", - "(Landroid/media/AudioAttributes;I)I", - android_media_AudioSystem_getVolumeIndexForAttributes), - MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes", - "(Landroid/media/AudioAttributes;)I", - android_media_AudioSystem_getMinVolumeIndexForAttributes), - MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes", - "(Landroid/media/AudioAttributes;)I", - android_media_AudioSystem_getMaxVolumeIndexForAttributes), - MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume), - MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume), - MAKE_AUDIO_SYSTEM_METHOD(setMasterMute), - MAKE_AUDIO_SYSTEM_METHOD(getMasterMute), - MAKE_AUDIO_SYSTEM_METHOD(setMasterMono), - MAKE_AUDIO_SYSTEM_METHOD(getMasterMono), - MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance), - MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance), - MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate), - MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount), - MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency), - MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice), - MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger), - MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V", - android_media_AudioSystem_setAudioFlingerBinder), - MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I", - android_media_AudioSystem_listAudioPorts), - MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I", - android_media_AudioSystem_getSupportedDeviceTypes), - MAKE_JNI_NATIVE_METHOD("createAudioPatch", - "([Landroid/media/AudioPatch;[Landroid/media/" - "AudioPortConfig;[Landroid/media/AudioPortConfig;)I", - android_media_AudioSystem_createAudioPatch), - MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I", - android_media_AudioSystem_releaseAudioPatch), - MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I", - android_media_AudioSystem_listAudioPatches), - MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", - android_media_AudioSystem_setAudioPortConfig), - MAKE_JNI_NATIVE_METHOD("startAudioSource", - "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", - android_media_AudioSystem_startAudioSource), - MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource), - MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession), - MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", - android_media_AudioSystem_registerPolicyMixes), - MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I", - android_media_AudioSystem_getRegisteredPolicyMixes), - MAKE_JNI_NATIVE_METHOD("updatePolicyMixes", - "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/" - "AudioMixingRule;)I", - android_media_AudioSystem_updatePolicyMixes), - MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", - android_media_AudioSystem_setUidDeviceAffinities), - MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities), - MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback", - android_media_AudioSystem_registerDynPolicyCallback), - MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback", - android_media_AudioSystem_registerRecordingCallback), - MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback", - android_media_AudioSystem_registerRoutingCallback), - MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback", - android_media_AudioSystem_registerVolRangeInitReqCallback), - MAKE_AUDIO_SYSTEM_METHOD(systemReady), - MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB), - MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support", - android_media_AudioSystem_getOffloadSupport), - MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I", - android_media_AudioSystem_getMicrophones), - MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I", - android_media_AudioSystem_getSurroundFormats), - MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", - android_media_AudioSystem_getReportedSurroundFormats), - MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled), - MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids), - MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids), - MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids), - MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported), - MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported), - MAKE_JNI_NATIVE_METHOD( - "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", - android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia), - MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages), - MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy), - MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled), - MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids), - MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported), - MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - android_media_AudioSystem_setDevicesRoleForStrategy), - MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - android_media_AudioSystem_removeDevicesRoleForStrategy), - MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy), - MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", - android_media_AudioSystem_getDevicesForRoleAndStrategy), - MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - android_media_AudioSystem_setDevicesRoleForCapturePreset), - MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - android_media_AudioSystem_addDevicesRoleForCapturePreset), - MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - android_media_AudioSystem_removeDevicesRoleForCapturePreset), - MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset), - MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", - android_media_AudioSystem_getDevicesForRoleAndCapturePreset), - MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes", - "(Landroid/media/AudioAttributes;[Landroid/media/" - "AudioDeviceAttributes;Z)I", - android_media_AudioSystem_getDevicesForAttributes), - MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", - android_media_AudioSystem_setUserIdDeviceAffinities), - MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities), - MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid), - MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I", - android_media_AudioSystem_setVibratorInfos), - MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer", - "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", - android_media_AudioSystem_getSpatializer), - MAKE_JNI_NATIVE_METHOD("canBeSpatialized", - "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" - "[Landroid/media/AudioDeviceAttributes;)Z", - android_media_AudioSystem_canBeSpatialized), - MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose", - "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", - android_media_AudioSystem_nativeGetSoundDose), - MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport", - "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", - android_media_AudioSystem_getDirectPlaybackSupport), - MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes", - "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", - android_media_AudioSystem_getDirectProfilesForAttributes), - MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I", - android_media_AudioSystem_getSupportedMixerAttributes), - MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes", - "(Landroid/media/AudioAttributes;IILandroid/media/" - "AudioMixerAttributes;)I", - android_media_AudioSystem_setPreferredMixerAttributes), - MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes", - "(Landroid/media/AudioAttributes;ILjava/util/List;)I", - android_media_AudioSystem_getPreferredMixerAttributes), - MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes", - "(Landroid/media/AudioAttributes;II)I", - android_media_AudioSystem_clearPreferredMixerAttributes), - MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency), - MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled), - MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled), - MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange", - "(Ljava/lang/String;Ljava/lang/Runnable;)J", - android_media_AudioSystem_listenForSystemPropertyChange), - MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate", - "(J)V", - android_media_AudioSystem_triggerSystemPropertyUpdate), - - }; +static const JNINativeMethod gMethods[] = { + MAKE_AUDIO_SYSTEM_METHOD(setParameters), + MAKE_AUDIO_SYSTEM_METHOD(getParameters), + MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone), + MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActive), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely), + MAKE_AUDIO_SYSTEM_METHOD(isSourceActive), + MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId), + MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", + android_media_AudioSystem_setDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange), + MAKE_AUDIO_SYSTEM_METHOD(setPhoneState), + MAKE_AUDIO_SYSTEM_METHOD(setForceUse), + MAKE_AUDIO_SYSTEM_METHOD(getForceUse), + MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled), + MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume), + MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex), + MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;IZI)I", + android_media_AudioSystem_setVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I", + android_media_AudioSystem_getVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMinVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMaxVolumeIndexForAttributes), + MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount), + MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency), + MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice), + MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger), + MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V", + android_media_AudioSystem_setAudioFlingerBinder), + MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPorts), + MAKE_JNI_NATIVE_METHOD("getSupportedDeviceTypes", "(ILandroid/util/IntArray;)I", + android_media_AudioSystem_getSupportedDeviceTypes), + MAKE_JNI_NATIVE_METHOD("createAudioPatch", + "([Landroid/media/AudioPatch;[Landroid/media/" + "AudioPortConfig;[Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_createAudioPatch), + MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I", + android_media_AudioSystem_releaseAudioPatch), + MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPatches), + MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_setAudioPortConfig), + MAKE_JNI_NATIVE_METHOD("startAudioSource", + "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_startAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession), + MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", + android_media_AudioSystem_registerPolicyMixes), + MAKE_JNI_NATIVE_METHOD("getRegisteredPolicyMixes", "(Ljava/util/List;)I", + android_media_AudioSystem_getRegisteredPolicyMixes), + MAKE_JNI_NATIVE_METHOD("updatePolicyMixes", + "([Landroid/media/audiopolicy/AudioMix;[Landroid/media/audiopolicy/" + "AudioMixingRule;)I", + android_media_AudioSystem_updatePolicyMixes), + MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUidDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback", + android_media_AudioSystem_registerDynPolicyCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback", + android_media_AudioSystem_registerRecordingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback", + android_media_AudioSystem_registerRoutingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback", + android_media_AudioSystem_registerVolRangeInitReqCallback), + MAKE_AUDIO_SYSTEM_METHOD(systemReady), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support", + android_media_AudioSystem_getOffloadSupport), + MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getMicrophones), + MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I", + android_media_AudioSystem_getSurroundFormats), + MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getReportedSurroundFormats), + MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported), + MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported), + MAKE_JNI_NATIVE_METHOD( + "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", + android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia), + MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages), + MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy), + MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids), + MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForStrategy), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndStrategy), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_addDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForCapturePreset), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes", + "(Landroid/media/AudioAttributes;[Landroid/media/" + "AudioDeviceAttributes;Z)I", + android_media_AudioSystem_getDevicesForAttributes), + MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid), + MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I", + android_media_AudioSystem_setVibratorInfos), + MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer", + "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_getSpatializer), + MAKE_JNI_NATIVE_METHOD("canBeSpatialized", + "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" + "[Landroid/media/AudioDeviceAttributes;)Z", + android_media_AudioSystem_canBeSpatialized), + MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose", + "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_nativeGetSoundDose), + MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport", + "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getDirectPlaybackSupport), + MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes", + "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", + android_media_AudioSystem_getDirectProfilesForAttributes), + MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I", + android_media_AudioSystem_getSupportedMixerAttributes), + MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;IILandroid/media/" + "AudioMixerAttributes;)I", + android_media_AudioSystem_setPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;ILjava/util/List;)I", + android_media_AudioSystem_getPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_clearPreferredMixerAttributes), + MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency), + MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled), + MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled), + MAKE_JNI_NATIVE_METHOD("listenForSystemPropertyChange", + "(Ljava/lang/String;Ljava/lang/Runnable;)J", + android_media_AudioSystem_listenForSystemPropertyChange), + MAKE_JNI_NATIVE_METHOD("triggerSystemPropertyUpdate", "(J)V", + android_media_AudioSystem_triggerSystemPropertyUpdate), +}; static const JNINativeMethod gEventHandlerMethods[] = {MAKE_JNI_NATIVE_METHOD("native_setup", "(Ljava/lang/Object;)V", diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index a939d9274956..755704a5ad91 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -115,6 +115,7 @@ static struct { jfieldID supportedDisplayModes; jfieldID activeDisplayModeId; jfieldID renderFrameRate; + jfieldID hasArrSupport; jfieldID supportedColorModes; jfieldID activeColorMode; jfieldID hdrCapabilities; @@ -1453,7 +1454,7 @@ static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jlong disp env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeDisplayModeId, info.activeDisplayModeId); env->SetFloatField(object, gDynamicDisplayInfoClassInfo.renderFrameRate, info.renderFrameRate); - + env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.hasArrSupport, info.hasArrSupport); jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size()); if (colorModesArray == NULL) { jniThrowException(env, "java/lang/OutOfMemoryError", NULL); @@ -2641,6 +2642,8 @@ int register_android_view_SurfaceControl(JNIEnv* env) GetFieldIDOrDie(env, dynamicInfoClazz, "activeDisplayModeId", "I"); gDynamicDisplayInfoClassInfo.renderFrameRate = GetFieldIDOrDie(env, dynamicInfoClazz, "renderFrameRate", "F"); + gDynamicDisplayInfoClassInfo.hasArrSupport = + GetFieldIDOrDie(env, dynamicInfoClazz, "hasArrSupport", "Z"); gDynamicDisplayInfoClassInfo.supportedColorModes = GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I"); gDynamicDisplayInfoClassInfo.activeColorMode = diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp index 06621c9e6ab3..bd403c7d9713 100644 --- a/core/jni/platform/host/HostRuntime.cpp +++ b/core/jni/platform/host/HostRuntime.cpp @@ -262,7 +262,8 @@ static void* mmapFile(const char* dataFilePath) { } // returns result from java.lang.System.getProperty -static string getJavaProperty(JNIEnv* env, const char* property_name) { +static string getJavaProperty(JNIEnv* env, const char* property_name, + const char* defaultValue = "") { jclass system = FindClassOrDie(env, "java/lang/System"); jmethodID getPropertyMethod = GetStaticMethodIDOrDie(env, system, "getProperty", @@ -270,7 +271,7 @@ static string getJavaProperty(JNIEnv* env, const char* property_name) { auto jString = (jstring)env->CallStaticObjectMethod(system, getPropertyMethod, env->NewStringUTF(property_name), - env->NewStringUTF("")); + env->NewStringUTF(defaultValue)); ScopedUtfChars chars(env, jString); return string(chars.c_str()); } @@ -430,7 +431,6 @@ public: } // namespace android -#ifndef _WIN32 using namespace android; JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { @@ -439,12 +439,14 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { return JNI_ERR; } - Vector<String8> args; - HostRuntime runtime; + string useBaseHostRuntime = getJavaProperty(env, "use_base_native_hostruntime", "true"); + if (useBaseHostRuntime == "true") { + Vector<String8> args; + HostRuntime runtime; - runtime.onVmCreated(env); - runtime.start("HostRuntime", args, false); + runtime.onVmCreated(env); + runtime.start("HostRuntime", args, false); + } return JNI_VERSION_1_6; } -#endif diff --git a/core/res/OWNERS b/core/res/OWNERS index 5293131180ae..d109cee5d910 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -60,6 +60,7 @@ per-file res/values/config_display.xml = file:/services/core/java/com/android/se # Wear per-file res/*-watch/* = file:/WEAR_OWNERS +per-file res/*-watch-v*/* = file:/WEAR_OWNERS # Performance per-file res/values/config.xml = file:/PERFORMANCE_OWNERS diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml new file mode 100644 index 000000000000..8b2afa86986c --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/disabledAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorPrimary" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml new file mode 100644 index 000000000000..cefc9121b7a4 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_text_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/primaryContentAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorOnPrimary" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml new file mode 100644 index 000000000000..eaf9e7d50bbd --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/disabledAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorSurfaceContainer" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml new file mode 100644 index 000000000000..94e50fbe2533 --- /dev/null +++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:alpha="?attr/primaryContentAlpha" + android:color="?attr/materialColorOnSurface" /> + <item android:state_enabled="true" + android:color="?attr/materialColorOnSurface" /> +</selector>
\ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml new file mode 100644 index 000000000000..0029de14e34a --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/btn_material_filled_background_color"/> + <corners android:radius="?android:attr/buttonCornerRadius"/> + <size + android:width="@dimen/btn_material_width" + android:height="@dimen/btn_material_height" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml new file mode 100644 index 000000000000..105f077cd841 --- /dev/null +++ b/core/res/res/drawable-watch-v36/btn_background_material_filled_tonal.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/btn_material_filled_tonal_background_color"/> + <corners android:radius="?android:attr/buttonCornerRadius"/> + <size + android:width="@dimen/btn_material_width" + android:height="@dimen/btn_material_height" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_zen_mode_icon_piano.xml b/core/res/res/drawable/ic_zen_mode_icon_piano.xml new file mode 100644 index 000000000000..012b9398d687 --- /dev/null +++ b/core/res/res/drawable/ic_zen_mode_icon_piano.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:tint="?android:attr/colorControlNormal" + android:viewportHeight="960" + android:viewportWidth="960"> + <path + android:fillColor="@android:color/white" + android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L330,760L330,580L320,580Q303,580 291.5,568.5Q280,557 280,540L280,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM630,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L680,200L680,540Q680,557 668.5,568.5Q657,580 640,580L630,580L630,760ZM390,760L570,760L570,580L560,580Q543,580 531.5,568.5Q520,557 520,540L520,200L440,200L440,540Q440,557 428.5,568.5Q417,580 400,580L390,580L390,760Z" /> +</vector>
\ No newline at end of file diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 1d205264af99..957e835bec6b 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1904,7 +1904,7 @@ <string name="managed_profile_label_badge_2" msgid="5673187309555352550">"Upaya ke-2 <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="managed_profile_label_badge_3" msgid="6882151970556391957">"Upaya ke-3 <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="clone_profile_label_badge" msgid="1871997694718793964">"Clone <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="private_profile_label_badge" msgid="1712086003787839183">"<xliff:g id="LABEL">%1$s</xliff:g> Pribadi"</string> + <string name="private_profile_label_badge" msgid="1712086003787839183">"<xliff:g id="LABEL">%1$s</xliff:g> Privasi"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Meminta PIN sebelum melepas sematan"</string> <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Meminta pola pembukaan kunci sebelum melepas sematan"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Meminta sandi sebelum melepas sematan"</string> diff --git a/packages/SystemUI/res/layout/udfps_bp_view.xml b/core/res/res/values-watch-v36/colors.xml index f1c55ef16cdc..4bc2a66fa206 100644 --- a/packages/SystemUI/res/layout/udfps_bp_view.xml +++ b/core/res/res/values-watch-v36/colors.xml @@ -1,6 +1,5 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2021 The Android Open Source Project + ~ Copyright (C) 2024 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,9 +13,6 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsBpView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view" - android:layout_width="match_parent" - android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsBpView> +<!-- TODO(b/372524566): update color token's value to match material3 design. --> +<resources> +</resources>
\ No newline at end of file diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml new file mode 100644 index 000000000000..c8f347afb318 --- /dev/null +++ b/core/res/res/values-watch-v36/config.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Overrides system value --> + <dimen name="config_buttonCornerRadius">26dp</dimen> +</resources> diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml new file mode 100644 index 000000000000..ad3c1a3ef3a1 --- /dev/null +++ b/core/res/res/values-watch-v36/dimens_material.xml @@ -0,0 +1,28 @@ +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <!-- values for material3 button --> + <dimen name="btn_material_width">172dp</dimen> + <dimen name="btn_material_height">52dp</dimen> + <dimen name="btn_horizontal_edge_padding">14dp</dimen> + <dimen name="btn_drawable_padding">6dp</dimen> + <dimen name="btn_lineHeight">18sp</dimen> + <dimen name="btn_textSize">15sp</dimen> + + <!-- Opacity factor for disabled material3 widget --> + <dimen name="disabled_alpha_device_default">0.12</dimen> + <dimen name="primary_content_alpha_device_default">0.38</dimen> +</resources> diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml new file mode 100644 index 000000000000..32a22bb755cb --- /dev/null +++ b/core/res/res/values-watch-v36/styles_material.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Button Styles --> + <!-- Material Button - Filled --> + <style name="Widget.DeviceDefault.Button.Filled" parent="Widget.DeviceDefault.Button"> + <item name="android:background">@drawable/btn_background_material_filled</item> + <item name="textAppearance">@style/TextAppearance.Widget.Button.Material.Filled</item> + </style> + + <!-- Material Button - Filled Tonal(Override system default button styles) --> + <style name="Widget.DeviceDefault.Button"> + <item name="background">@drawable/btn_background_material_filled_tonal</item> + <item name="textAppearance">@style/TextAppearance.Widget.Button.Material</item> + <item name="minHeight">@dimen/btn_material_height</item> + <item name="maxWidth">@dimen/btn_material_width</item> + <item name="android:paddingStart">@dimen/btn_horizontal_edge_padding</item> + <item name="android:paddingEnd">@dimen/btn_horizontal_edge_padding</item> + <item name="android:drawablePadding">@dimen/btn_drawable_padding</item> + <item name="android:maxLines">2</item> + <item name="android:ellipsize">end</item> + <item name="android:breakStrategy">simple</item> + <item name="stateListAnimator">@anim/button_state_list_anim_material</item> + <item name="focusable">true</item> + <item name="clickable">true</item> + <item name="gravity">center_vertical</item> + </style> + + <!-- Text Styles --> + <!-- TextAppearance for Material Button - Filled --> + <style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material"> + <item name="textColor">@color/btn_material_filled_text_color</item> + </style> + + <!-- TextAppearance for Material Button - Filled Tonal --> + <style name="TextAppearance.Widget.Button.Material" parent="TextAppearance.DeviceDefault"> + <item name="android:fontFamily">font-family-flex-device-default</item> + <item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item> + <item name="textSize">@dimen/btn_textSize</item> + <item name="textColor">@color/btn_material_filled_tonal_text_color</item> + <item name="lineHeight">@dimen/btn_lineHeight</item> + </style> +</resources>
\ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8a2d767e9ad1..169cf594f42e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4447,17 +4447,25 @@ <string-array translatable="false" name="config_defaultPinnerServiceFiles"> </string-array> - <!-- True if camera app should be pinned via Pinner Service --> + <!-- Note: This config is deprecated, use config_pinnerCameraPinBytes instead. + True if camera app should be pinned via Pinner Service --> <bool name="config_pinnerCameraApp">false</bool> - <!-- Bytes that the PinnerService will pin for Home app --> - <integer name="config_pinnerHomePinBytes">0</integer> + <!-- Default: 60 MB. Bytes that the PinnerService will pin for Home app --> + <integer name="config_pinnerHomePinBytes">62914560</integer> - <!-- True if assistant app should be pinned via Pinner Service --> + <!-- Default: 80 MB. Bytes that the PinnerService will pin for Camera app --> + <integer name="config_pinnerCameraPinBytes">83886080</integer> + + <!-- Note: This config is deprecated, use config_pinnerAssistantPinBytes instead. + True if assistant app should be pinned via Pinner Service --> <bool name="config_pinnerAssistantApp">false</bool> - <!-- Bytes that the PinnerService will pin for WebView --> - <integer name="config_pinnerWebviewPinBytes">0</integer> + <!-- Default: 60 MB. Bytes that the PinnerService will pin for Assistant --> + <integer name="config_pinnerAssistantPinBytes">62914560</integer> + + <!-- Default: 20 MB. Bytes that the PinnerService will pin for WebView --> + <integer name="config_pinnerWebviewPinBytes">20971520</integer> <!-- Maximum memory that PinnerService will pin for apps expressed as a percentage of total device memory [0,100]. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 12b444bb3978..ab1b49173921 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3471,6 +3471,8 @@ <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> <java-symbol type="bool" name="config_pinnerCameraApp" /> <java-symbol type="integer" name="config_pinnerHomePinBytes" /> + <java-symbol type="integer" name="config_pinnerCameraPinBytes" /> + <java-symbol type="integer" name="config_pinnerAssistantPinBytes" /> <java-symbol type="bool" name="config_pinnerAssistantApp" /> <java-symbol type="integer" name="config_pinnerWebviewPinBytes" /> <java-symbol type="integer" name="config_pinnerMaxPinnedMemoryPercentage" /> diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index 48e26203fab4..23a09857032c 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -2397,10 +2397,25 @@ public class NotificationTest { public void progressStyle_getProgressMax_returnsSumOfSegmentLength() { final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle(); progressStyle + .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15), + new Notification.ProgressStyle.Segment(25))) .addProgressSegment(new Notification.ProgressStyle.Segment(10)) .addProgressSegment(new Notification.ProgressStyle.Segment(20)); - assertThat(progressStyle.getProgressMax()).isEqualTo(30); + assertThat(progressStyle.getProgressMax()).isEqualTo(70); + } + + @Test + @EnableFlags(Flags.FLAG_API_RICH_ONGOING) + public void progressStyle_getProgressMax_onSetProgressSegments_resets() { + final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle(); + progressStyle + .addProgressSegment(new Notification.ProgressStyle.Segment(10)) + .addProgressSegment(new Notification.ProgressStyle.Segment(20)) + .setProgressSegments(List.of(new Notification.ProgressStyle.Segment(15), + new Notification.ProgressStyle.Segment(25))); + + assertThat(progressStyle.getProgressMax()).isEqualTo(40); } @Test diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 0621d82747e5..2880ecf835c4 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -16,6 +16,8 @@ package android.widget; +import static android.appwidget.flags.Flags.remoteAdapterConversion; + import static com.android.internal.R.id.pending_intent_tag; import static org.junit.Assert.assertArrayEquals; @@ -282,7 +284,10 @@ public class RemoteViewsTest { widget.addView(view); ListView listView = (ListView) view.findViewById(R.id.list); - listView.onRemoteAdapterConnected(); + + if (!remoteAdapterConversion()) { + listView.onRemoteAdapterConnected(); + } assertNotNull(listView.getAdapter()); top.reapply(mContext, view); @@ -414,6 +419,58 @@ public class RemoteViewsTest { assertNotNull(view.findViewById(R.id.light_background_text)); } + @Test + public void remoteCollectionItemsAdapter_lightBackgroundLayoutFlagSet() { + AppWidgetHostView widget = new AppWidgetHostView(mContext); + RemoteViews container = new RemoteViews(mPackage, R.layout.remote_view_host); + RemoteViews listRemoteViews = new RemoteViews(mPackage, R.layout.remote_views_list); + RemoteViews item = new RemoteViews(mPackage, R.layout.remote_views_text); + item.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text); + listRemoteViews.setRemoteAdapter( + R.id.list, + new RemoteViews.RemoteCollectionItems.Builder().addItem(0, item).build()); + container.addView(R.id.container, listRemoteViews); + + widget.setOnLightBackground(true); + widget.updateAppWidget(container); + + // Populate the list view + ListView listView = (ListView) widget.findViewById(R.id.list); + int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY); + listView.measure(measureSpec, measureSpec); + listView.layout(0, 0, 100, 100); + + // Picks the light background layout id for the item + assertNotNull(listView.getChildAt(0).findViewById(R.id.light_background_text)); + assertNull(listView.getChildAt(0).findViewById(R.id.text)); + } + + @Test + public void remoteCollectionItemsAdapter_lightBackgroundLayoutFlagNotSet() { + AppWidgetHostView widget = new AppWidgetHostView(mContext); + RemoteViews container = new RemoteViews(mPackage, R.layout.remote_view_host); + RemoteViews listRemoteViews = new RemoteViews(mPackage, R.layout.remote_views_list); + RemoteViews item = new RemoteViews(mPackage, R.layout.remote_views_text); + item.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text); + listRemoteViews.setRemoteAdapter( + R.id.list, + new RemoteViews.RemoteCollectionItems.Builder().addItem(0, item).build()); + container.addView(R.id.container, listRemoteViews); + + widget.setOnLightBackground(false); + widget.updateAppWidget(container); + + // Populate the list view + ListView listView = (ListView) widget.findViewById(R.id.list); + int measureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY); + listView.measure(measureSpec, measureSpec); + listView.layout(0, 0, 100, 100); + + // Does not pick the light background layout id for the item + assertNotNull(listView.getChildAt(0).findViewById(R.id.text)); + assertNull(listView.getChildAt(0).findViewById(R.id.light_background_text)); + } + private RemoteViews createViewChained(int depth, String... texts) { RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host); diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index b001cc2929e3..46bd73e316f6 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -21,6 +21,7 @@ import static android.window.OnBackInvokedDispatcher.PRIORITY_OVERLAY; import static android.window.OnBackInvokedDispatcher.PRIORITY_SYSTEM_NAVIGATION_OBSERVER; import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER; +import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -486,6 +487,7 @@ public class WindowOnBackInvokedDispatcherTest { } @Test + @RequiresFlagsDisabled(FLAG_PREDICTIVE_BACK_TIMESTAMP_API) public void onBackInvoked_notCalledAfterCallbackUnregistration() throws RemoteException, InterruptedException { // Setup a callback that unregisters itself after the gesture is finished but before the @@ -684,6 +686,7 @@ public class WindowOnBackInvokedDispatcherTest { return new BackMotionEvent( /* touchX = */ 0, /* touchY = */ 0, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java index e3a1d8ac48e2..6ad2f088ce95 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java @@ -148,8 +148,12 @@ class BackupHelper { final TaskFragmentInfo info = mTaskFragmentInfos.valueAt(i); mPresenter.deleteTaskFragment(wct, info.getFragmentToken()); } - mPresenter.setSavedState(new Bundle()); + removeSavedState(); + } + + private void removeSavedState() { + mPresenter.setSavedState(new Bundle()); mParcelableTaskContainerDataList.clear(); mTaskFragmentInfos.clear(); mTaskFragmentParentInfos.clear(); @@ -169,6 +173,19 @@ class BackupHelper { return false; } + if (mTaskFragmentParentInfos.size() == 0) { + // No Task left in the WM hierarchy, remove the states and no need to restore. + if (DEBUG) Log.d(TAG, "Remove save states due to no task to restore."); + removeSavedState(); + return false; + } + + final ArrayList<Integer> taskIdsInSystem = new ArrayList<>(); + for (int i = mTaskFragmentParentInfos.size() - 1; i >= 0; --i) { + final TaskFragmentParentInfo parentInfo = mTaskFragmentParentInfos.valueAt(i); + taskIdsInSystem.add(parentInfo.getTaskId()); + } + if (DEBUG) Log.d(TAG, "Rebuilding TaskContainers."); final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>(); for (EmbeddingRule rule : rules) { @@ -190,6 +207,14 @@ class BackupHelper { } mParcelableTaskContainerDataList.remove(parcelableTaskContainerData); + if (!taskIdsInSystem.contains(parcelableTaskContainerData.mTaskId)) { + if (DEBUG) { + Log.d(TAG, "Rebuilding TaskContainer abort! Not existed. Task#" + + parcelableTaskContainerData.mTaskId); + } + continue; + } + final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData, mController, mTaskFragmentInfos); if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 8345b409ae52..3368e2eab3ad 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -239,6 +239,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen mActivityStartMonitor = new ActivityStartMonitor(); instrumentation.addMonitor(mActivityStartMonitor); foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener()); + + synchronized (mLock) { + // Abort the restoration if any and the application already has running activities. + abortRebuildingTaskContainersIfNeeded(null /* launchingActivity */); + } } private class FoldingFeatureListener @@ -285,6 +290,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } + if (abortRebuildingTaskContainersIfNeeded(null /* launchingActivity */)) { + return; + } + try { final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); @@ -2883,6 +2892,53 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } + @GuardedBy("mLock") + private boolean abortRebuildingTaskContainersIfNeeded(@Nullable Activity launchingActivity) { + if (mPresenter == null || !mPresenter.isWaitingToRebuildTaskContainers()) { + return false; + } + + final ActivityThread activityThread = ActivityThread.currentActivityThread(); + if (activityThread == null) { + return false; + } + + final Activity lastCreatedActivity = activityThread.getLastCreatedActivity(); + final Activity activity = + launchingActivity != null ? launchingActivity : lastCreatedActivity; + if (activity == null) { + return false; + } + + Log.w(TAG, "Rebuilding aborted, clean up."); + + // Retrieve the Task intent. + final int taskId = getTaskId(activity); + + // Clean up and abort the restoration + // TODO(b/369488857): also to remove the non-organized activities in the Task? + final TransactionRecord transactionRecord = + mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + mPresenter.abortTaskContainerRebuilding(wct); + transactionRecord.apply(false /* shouldApplyIndependently */); + + // Start the Task root activity if the task is now empty. + ActivityManager.RecentTaskInfo taskInfo = null; + final ActivityManager am = activity.getSystemService(ActivityManager.class); + final List<ActivityManager.AppTask> appTasks = am.getAppTasks(); + for (ActivityManager.AppTask appTask : appTasks) { + if (appTask.getTaskInfo().taskId == taskId) { + taskInfo = appTask.getTaskInfo(); + break; + } + } + if (taskInfo != null && !taskInfo.isRunning) { + activity.startActivity(taskInfo.baseIntent.cloneFilter()); + } + return true; + } + private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter { @Override @@ -2894,33 +2950,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } synchronized (mLock) { - if (mPresenter.isWaitingToRebuildTaskContainers()) { - Log.w(TAG, "Rebuilding aborted, clean up and restart"); - - // Retrieve the Task intent. - final int taskId = getTaskId(activity); - Intent taskIntent = null; - final ActivityManager am = activity.getSystemService(ActivityManager.class); - final List<ActivityManager.AppTask> appTasks = am.getAppTasks(); - for (ActivityManager.AppTask appTask : appTasks) { - if (appTask.getTaskInfo().taskId == taskId) { - taskIntent = appTask.getTaskInfo().baseIntent.cloneFilter(); - break; - } - } - - // Clean up and abort the restoration - // TODO(b/369488857): also to remove the non-organized activities in the Task? - final TransactionRecord transactionRecord = - mTransactionManager.startNewTransaction(); - final WindowContainerTransaction wct = transactionRecord.getTransaction(); - mPresenter.abortTaskContainerRebuilding(wct); - transactionRecord.apply(false /* shouldApplyIndependently */); - - // Start the Task root activity. - if (taskIntent != null) { - activity.startActivity(taskIntent); - } + if (abortRebuildingTaskContainersIfNeeded(activity)) { return; } diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index 582c1a22a203..be5a74b7be4d 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Spring na links"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Spring na regs"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Maak By Verstek Oop-instellings"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Kies hoe om webskakels vir hierdie app oop te maak"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In die app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In jou blaaier"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index 0798c9a8fbe0..8c3e3faa2042 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ወደ ግራ አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ወደ ቀኝ አሳድግ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"በነባሪ ቅንብሮች ክፈት"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ለዚህ የድር መተግበሪያ አገናኙን እንዴት እንደሚከፍቱ ይምረጡ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"በመተግበሪያው ውስጥ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"በአሳሽዎ ውስጥ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"እሺ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 9dcb5ec26273..4a34ce6ad5cf 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"تكبير"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"المحاذاة إلى اليسار"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"المحاذاة إلى اليمين"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"إعدادات الفتح تلقائيًا"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"اختيار طريقة فتح روابط الويب لهذا التطبيق"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"في التطبيق"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"في المتصفِّح"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"حسنًا"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 484eef53e087..94c9ba37c7dc 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"মেক্সিমাইজ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাওঁফাললৈ স্নেপ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"সোঁফাললৈ স্নেপ কৰক"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফ’ল্ট ছেটিং খোলক"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"এই এপ্টোৰ বাবে কিদৰে ৱেব লিংক খুলিব পাৰি সেয়া বাছনি কৰক"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"এপ্টোত"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপোনাৰ ব্ৰাউজাৰত"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index ded6da80464d..191d074ee714 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Böyüdün"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tərəf çəkin"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tərəf çəkin"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Defolt ayarlarla açın"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu tətbiq üçün veb-linklərin necə açılacağını seçin"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Tətbiqdə"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerinizdə"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 415547c790f4..852c90e535a1 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Uvećajte"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prikačite levo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prikačite desno"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Podešavanje Podrazumevano otvaraj"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja veb-linkova za ovu aplikaciju"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledaču"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Potvrdi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index aded647d43a1..661b6c7aa920 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Разгарнуць"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Размясціць злева"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Размясціць справа"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Налады параметра \"Адкрываць стандартна\""</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Выберыце, як гэта праграма будзе адкрываць вэб-спасылкі"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У праграме"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У браўзеры"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index 8a69176f2b6f..d6da2a8703f3 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увеличаване"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прилепване наляво"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прилепване надясно"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Отваряне на настройките по подразбиране"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Изберете как да се отварят уеб връзките за това приложение"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложението"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузъра ви"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index 3799c9fd1dca..62e26b11fa21 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"বড় করুন"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"বাঁদিকে স্ন্যাপ করুন"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ডানদিকে স্ন্যাপ করুন"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ডিফল্ট হিসেবে থাকা সেটিংস খুলুন"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"এই অ্যাপের জন্য কীভাবে ওয়েব লিঙ্ক খুলবেন তা বেছে নিন"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"অ্যাপের মধ্যে"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপনার ব্রাউজারে"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index f0d172ad20d0..15b6058c3196 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziranje"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pomicanje ulijevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pomicanje udesno"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvaranje prema zadanim postavkama"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja web linkova za ovu aplikaciju"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Uredu"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index bf35c90f89ab..7cc65a7b075e 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximitza"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajusta a l\'esquerra"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajusta a la dreta"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configuració d\'obertura predeterminada"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Tria com vols obrir els enllaços web per a aquesta aplicació"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"A l\'aplicació"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Al navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"D\'acord"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index 7fc1033b71be..f8bdcafd8645 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovat"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Přichytit vlevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Přichytit vpravo"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otevírat podle výchozího nastavení"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Určete, jak se v této aplikaci mají otevírat webové odkazy"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaci"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prohlížeči"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 717e6c42dd51..1e05069f739a 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimér"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fastgør til venstre"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fastgør til højre"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Indstillinger for automatisk åbning"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Vælg, hvordan denne app skal åben weblinks"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I din browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 1039273978e1..6466215c2865 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Μεγιστοποίηση"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Κούμπωμα αριστερά"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Κούμπωμα δεξιά"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Άνοιγμα ρυθμίσεων από προεπιλογή"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Επιλογή τρόπου ανοίγματος συνδέσμων ιστού για την εφαρμογή"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Στην εφαρμογή"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Στο πρόγραμμα περιήγησής σας"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ΟΚ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 98da627a4434..018291533e41 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml index e928fe02fbcf..9d581092748b 100644 --- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 98da627a4434..018291533e41 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 98da627a4434..018291533e41 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximise"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml index e48a9dbc2ebb..54346da994c1 100644 --- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximize"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Snap left"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Snap right"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Open by default settings"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Choose how to open web links for this app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index f349cbb1aeed..630d8067f381 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar a la izquierda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar a la derecha"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir con la configuración predeterminada"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Elige cómo abrir vínculos web para esta app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En un navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index b2b06d6db5d2..25c068b1fa43 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimeeri"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Tõmmake vasakule"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Tõmmake paremale"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avamisviisi vaikeseaded"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Valige, kuidas avada selle rakenduse puhul veebilinke"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Rakenduses"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauseris"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 4a71c49b8d8a..ba4cbc88120b 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizatu"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ezarri ezkerrean"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ezarri eskuinean"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Modu lehenetsian irekitzearen ezarpenak"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Aukeratu nola ireki sareko estekak aplikazio honetan"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Aplikazioan"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Arakatzailean"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ados"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index 941ff84b7799..95bad9cce976 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بزرگ کردن"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"کشیدن بهچپ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"کشیدن بهراست"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"تنظیمات باز کردن بهطور پیشفرض"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"انتخاب روش باز کردن پیوندهای وب مربوط به این برنامه"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"در برنامه"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"در مرورگر"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"تأیید"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index a45e9afeabfc..9c841afc9983 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Suurenna"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Siirrä vasemmalle"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Siirrä oikealle"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Avaa oletusasetusten mukaan"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Valitse, miten verkkolinkit avataan tässä sovelluksessa"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sovelluksessa"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Selaimella"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index ab972f94af22..0cc559f79dfa 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Axustar á esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Axustar á dereita"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Abrir coa configuración predeterminada"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escoller como abrir as ligazóns web para esta aplicación"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na aplicación"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 0dc2f73f1134..460f8709ccc0 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"મોટું કરો"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ડાબે સ્નૅપ કરો"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"જમણે સ્નૅપ કરો"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"આ ઍપ માટે વેબ લિંક ખોલવાની રીત પસંદ કરો"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ઍપમાં"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"તમારા બ્રાઉઝરમાં"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ઓકે"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 679d800a4dd2..17ceca187b39 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"बड़ा करें"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बाईं ओर स्नैप करें"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दाईं ओर स्नैप करें"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफ़ॉल्ट सेटिंग के हिसाब से खोलें"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"इस ऐप्लिकेशन के लिए वेब लिंक खोलने का तरीका चुनें"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ऐप्लिकेशन में"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"आपके ब्राउज़र में"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठीक है"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 1e5ffc858b86..20fa5468e296 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiziraj"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Poravnaj lijevo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Poravnaj desno"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvori prema zadanim postavkama"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Odaberite način otvaranja web-veza za ovu aplikaciju"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"U redu"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index 7c90a1924214..78cec15cc44a 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Teljes méret"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Balra igazítás"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Jobbra igazítás"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Alapértelmezett beállítások megnyitása"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Az app webes linkjeinek megnyitásához használt módszer"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Az alkalmazásban"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"A böngészőben"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index e8ed4ac9c9a4..1461322da16e 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ծավալել"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ամրացնել ձախ կողմում"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ամրացնել աջ կողմում"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Բացել կարգավորումներն ըստ կանխադրման"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Ընտրեք՝ ինչպես բացել այս հավելվածի վեբ հղումները"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Հավելվածում"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Ձեր դիտարկիչում"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Եղավ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 06b1634917e9..23626e49b8db 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimalkan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Maksimalkan ke kiri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Maksimalkan ke kanan"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka dengan setelan default"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pilih cara membuka link web untuk aplikasi ini"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Di aplikasi"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Di browser Anda"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Oke"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index 2d447771de25..6ae4c7c60bc7 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Stækka"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Smella til vinstri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Smella til hægri"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Stillingar sjálfvirkrar opnunar"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Veldu hvernig veftenglar opnast í forritinu"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Í forritinu"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Í vafranum"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Í lagi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index fa072639f089..41765e3c56d5 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"הגדלה"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"הצמדה לשמאל"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"הצמדה לימין"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"הגדרות לפתיחה כברירת מחדל"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"בחירת האופן שבו קישורים לדפי אינטרנט אחרים ייפתחו באפליקציה הזו"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"באפליקציה"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"בדפדפן"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"אישור"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 91667c00ce12..c7dfd457950a 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"左にスナップ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"右にスナップ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"デフォルトの設定で開く"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"このアプリのウェブリンクを開く方法を選択"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"アプリ内"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ブラウザ内"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index 2208348485af..4986c2d63761 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"მაქსიმალურად გაშლა"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"მარცხნივ გადატანა"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"მარჯვნივ გადატანა"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"პარამეტრების ნაგულისხმევად გახსნა"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ამ აპისთვის ვებ ბმულების გახსნის წესის არჩევა"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"აპში"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"თქვენს ბრაუზერში"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"კარგი"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 416a84c86281..7c49ae5abf4e 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Жаю"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солға тіркеу"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңға тіркеу"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Әдепкісінше ашу параметрлері"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Осы қолданбадағы веб-сілтемелерді ашу жолын таңдаңыз"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Қолданбада"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Браузерде"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайды"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index b074d65700f4..c6af12528d93 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ពង្រីក"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ផ្លាស់ទីទៅឆ្វេង"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ផ្លាស់ទីទៅស្ដាំ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ការកំណត់បើកតាមលំនាំដើម"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ជ្រើសរើសរបៀបបើកតំណបណ្ដាញសម្រាប់កម្មវិធីនេះ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"នៅក្នុងកម្មវិធី"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"នៅក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិតរបស់អ្នក"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"យល់ព្រម"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 9c22241f5037..498ad0f04a5e 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ಎಡಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ಬಲಕ್ಕೆ ಸ್ನ್ಯಾಪ್ ಮಾಡಿ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ಡೀಫಾಲ್ಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳಿಂದ ತೆರೆಯಿರಿ"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ಈ ಆ್ಯಪ್ಗೆ ವೆಬ್ ಲಿಂಕ್ಗಳನ್ನು ಹೇಗೆ ತೆರೆಯಬೇಕು ಎಂಬುದನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ಆ್ಯಪ್ನಲ್ಲಿ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ನಿಮ್ಮ ಬ್ರೌಸರ್ನಲ್ಲಿ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ಸರಿ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index 58dd6f8e7e99..a92d3cb21b76 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"최대화하기"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"왼쪽으로 맞추기"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"오른쪽으로 맞추기"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"기본값으로 열기 설정"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"이 앱에서 웹 링크를 여는 방법을 선택하세요"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"앱에서"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"브라우저에서"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"확인"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 75feedea1b89..b01659d95407 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Чоңойтуу"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Солго жылдыруу"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Оңго жылдыруу"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Демейки шартта ачуу параметрлери"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Колдонмодо шилтемелер кантип ачылышы керек экенин тандаңыз"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Колдонмодо"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Серепчиңизде"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайт"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index 8f28504b41b4..f3e7169e1431 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ແນບຊ້າຍ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ແນບຂວາ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ເປີດຕາມການຕັ້ງຄ່າເລີ່ມຕົ້ນ"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ເລືອກວິທີເປີດລິ້ງເວັບສຳລັບແອັບນີ້"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ໃນແອັບ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ໃນໂປຣແກຣມທ່ອງເວັບຂອງທ່ານ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ຕົກລົງ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index b97b8787ed10..719cf60d18e2 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Padidinti"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pritraukti kairėje"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pritraukti dešinėje"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atidaryti pagal numatytuosius nustatymus"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pasirinkite, kaip atidaryti šios programos žiniatinklio nuorodas"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Programoje"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Naršyklėje"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Gerai"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index de200d816e7a..1649a2e54c12 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimizēt"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Piestiprināt pa kreisi"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Piestiprināt pa labi"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Atvērt pēc noklusējuma iestatījumiem"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Izvēlieties, kā atvērt šajā lietotnē norādītās saites"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Lietotnē"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pārlūkprogrammā"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Labi"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 61277d6765fd..4df5d6f0fc8e 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"വലുതാക്കുക"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ഇടതുവശത്തേക്ക് സ്നാപ്പ് ചെയ്യുക"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"വലതുവശത്തേക്ക് സ്നാപ്പ് ചെയ്യുക"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ഡിഫോൾട്ട് ക്രമീകരണം ഉപയോഗിച്ച് തുറക്കുക"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ഈ ആപ്പിനായി വെബ് ലിങ്കുകൾ എങ്ങനെ തുറക്കണമെന്ന് തിരഞ്ഞെടുക്കൂ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ആപ്പിൽ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"നിങ്ങളുടെ ബ്രൗസറിൽ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ശരി"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 2b313a2be17c..70f6a0806468 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -98,7 +98,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string> <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Аппын цэсийг нээхийн тулд товшино уу"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Олон аппыг хамтад нь харуулахын товшино уу"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Олон аппыг хамтад нь харуулахын тулд товшино уу"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Аппын цэсээс бүтэн дэлгэц рүү буцна уу"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Дэлгэц хуваах горимд ашиглахын тулд өөр аппыг чирнэ үү"</string> @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Томруулах"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Зүүн тийш зэрэгцүүлэх"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Баруун тийш зэрэгцүүлэх"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Өгөгдмөл тохиргоогоор нээх"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Энэ аппад веб холбоосыг хэрхэн нээхийг сонгоно уу"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Аппад"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Хөтчидөө"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index 9778dcd0a166..e2844861e31a 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"मोठे करा"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"डावीकडे स्नॅप करा"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"उजवीकडे स्नॅप करा"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"बाय डीफॉल्ट सेटिंग्ज उघडा"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"या अॅपसाठीच्या वेब लिंक कशा उघडाव्यात हे निवडा"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ॲपमध्ये"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तुमच्या ब्राउझरमध्ये"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ओके"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index 0ee439657e2a..59032fb91d92 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimumkan"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Autojajar ke kiri"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Autojajar ke kanan"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buka tetapan secara lalai"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Pilih cara membuka pautan web untuk apl ini"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Pada apl"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pada penyemak imbas"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 57d09500b3bf..7b952c1f3fa8 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ချဲ့ရန်"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ဘယ်တွင် ချဲ့ရန်"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ညာတွင် ချဲ့ရန်"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"မူရင်းဆက်တင်ဖြင့် ဖွင့်ရန်"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ဤအက်ပ်အတွက် ဝဘ်လင့်ခ်များ မည်သို့ဖွင့်မည်ကို ရွေးပါ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"အက်ပ်တွင်"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"သင်၏ဘရောင်ဇာတွင်"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index eb505ec43a65..8733a7f7f7ef 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimer"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fest til venstre"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fest til høyre"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Innstillinger for åpning som standard"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Velg hvordan nettlinker skal åpnes for denne appen"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I nettleseren"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 47d66d5deec8..cdd2a1f80d1d 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ठुलो बनाउनुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"बायाँतिर स्न्याप गर्नुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"दायाँतिर स्न्याप गर्नुहोस्"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"डिफल्ट सेटिङअनुसार खोल्नुहोस्"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"यो एपका वेब लिंकहरू खोल्ने तरिका छनौट गर्नुहोस्"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"एपमा"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तपाईंको ब्राउजरमा"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठिक छ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index e3e344124e0b..d8a285649952 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximaliseren"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Links uitlijnen"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Rechts uitlijnen"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Instellingen voor Standaard openen"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Kies hoe je weblinks voor deze app wilt openen"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In de app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In je browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index 2c29c7f80411..06e1f54e9e9b 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ਵੱਡਾ ਕਰੋ"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ਖੱਬੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"ਸੱਜੇ ਪਾਸੇ ਸਨੈਪ ਕਰੋ"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੈਟਿੰਗਾਂ ਮੁਤਾਬਕ ਖੋਲ੍ਹੋ"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ਇਸ ਐਪ ਲਈ ਵੈੱਬ ਲਿੰਕਾਂ ਨੂੰ ਖੋਲ੍ਹਣ ਦਾ ਤਰੀਕਾ ਚੁਣੋ"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ਐਪ ਵਿੱਚ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ਤੁਹਾਡੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ਠੀਕ ਹੈ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 1e7b18117812..90d374cb446c 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksymalizuj"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Przyciągnij do lewej"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Przyciągnij do prawej"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Ustawienia domyślnego otwierania"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Wybierz, gdzie chcesz otwierać linki z tej aplikacji"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"W aplikacji"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"W przeglądarce"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 7d728a03b4ec..a7cf9d87dbd3 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para este app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index 752fd6fb8970..40241b4710ce 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Encaixar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Encaixar à direita"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Definições de Abrir por predefinição"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para esta app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 7d728a03b4ec..a7cf9d87dbd3 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizar"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Ajustar à esquerda"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Ajustar à direita"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Configurações \"Abrir por padrão\""</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Escolha como abrir links da Web para este app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 3985d9bc792a..f323ff8f5109 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximizează"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Trage la stânga"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Trage la dreapta"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Setări de deschidere în mod prestabilit"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Alege modul de deschidere a linkurilor web pentru aplicație"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"În aplicație"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"În browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index 58a196b4eecb..45da723e5e97 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Развернуть"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Привязать слева"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Привязать справа"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Настройки, регулирующие, как по умолчанию открываются ссылки"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Выберите, где будут открываться ссылки из этого приложения"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложении"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузере"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 6682b017f2cc..0b1a2390b892 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"විහිදන්න"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"වමට ස්නැප් කරන්න"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"දකුණට ස්නැප් කරන්න"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"පෙරනිමි සැකසීම් මඟින් විවෘත කරන්න"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"මෙම යෙදුම සඳහා වෙබ් සබැඳි විවෘත කරන ආකාරය තෝරා ගන්න"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"යෙදුම තුළ"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ඔබේ බ්රව්සරය තුළ"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"හරි"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index 96e54f1caa68..7d1a408f3172 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maximalizovať"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Prichytiť vľavo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Prichytiť vpravo"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Otvárať podľa predvolených nastavení"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Vyberte, ako sa majú v tejto aplikácii otvárať webové odkazy"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikácii"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prehliadači"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 66c9b26de54e..a50397f0e973 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Maksimiraj"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Pripni levo"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Pripni desno"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Nastavitve privzetega odpiranja"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Izberite način odpiranja spletnih povezav za to aplikacijo"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaciji"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V brskalniku"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"V redu"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 6e49999545a8..480e2a4d7f68 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -98,7 +98,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string> <string name="windowing_app_handle_education_tooltip" msgid="6398482412956375783">"Trokit për të hapur menynë e aplikacionit"</string> - <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Trokit për të shfaqur disa aplikacone bashkë"</string> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="6285279585554484957">"Trokit për të shfaqur disa aplikacione bashkë"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="6685429075790085337">"Kthehu tek ekrani i plotë nga menyja e aplikacionit"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index bd2fb8c442f3..d8debc01cae2 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Увећајте"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Прикачите лево"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Прикачите десно"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Подешавање Подразумевано отварај"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Одаберите начин отварања веб-линкова за ову апликацију"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У апликацији"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У прегледачу"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Потврди"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 2184ac6bfca7..e262a9b477d3 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Utöka"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Fäst till vänster"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Fäst till höger"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Inställningar för öppna som standard"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Välj hur webblänkar ska öppnas för den här appen"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I webbläsaren"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index 6068bf00a6df..b1679c078aac 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Panua"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Telezesha kushoto"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Telezesha kulia"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Fungua kwa mipangilio chaguomsingi"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Chagua jinsi ya kufungua viungo vya wavuti vya programu hii"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Kwenye programu"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kwenye kivinjari chako"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Sawa"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index a14abac75245..8df170df794b 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"பெரிதாக்கும்"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"இடதுபுறம் நகர்த்தும்"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"வலதுபுறம் நகர்த்தும்"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"இயல்பாக அமைப்புகளைத் திறக்கும்"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"இந்த ஆப்ஸில் வலை இணைப்புகளைத் திறக்கும் வழிமுறையைத் தேர்வுசெய்யுங்கள்"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ஆப்ஸில்"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"உங்கள் பிரவுசரில்"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"சரி"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 84e76a8cc361..82523b68308a 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"మ్యాగ్జిమైజ్ చేయండి"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"ఎడమ వైపున స్నాప్ చేయండి"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"కుడి వైపున స్నాప్ చేయండి"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"ఆటోమేటిక్ సెట్టింగ్ల ద్వారా తెరవండి"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"ఈ యాప్నకు సంబంధించిన వెబ్ లింక్లను ఎలా తెరవాలో ఎంచుకోండి"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"యాప్లో"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"మీ బ్రౌజర్లో"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"సరే"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index 856893f83955..cc7a60384bf6 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"ขยายใหญ่สุด"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"จัดพอดีกับทางซ้าย"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"จัดพอดีกับทางขวา"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"เปิดตามการตั้งค่าเริ่มต้น"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"เลือกวิธีเปิดเว็บลิงก์สำหรับแอปนี้"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ในแอป"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ในเบราว์เซอร์"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ตกลง"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index dc92efd8e18f..bb543f3f6ee9 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"I-maximize"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"I-snap pakaliwa"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"I-snap pakanan"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Buksan sa pamamagitan ng mga default na setting"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Piliin kung paano magbukas ng web link para sa app na ito"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sa app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Sa iyong browser"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index e206cd6cd10c..c8dcefbe3728 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Ekranı kapla"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Sola tuttur"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Sağa tuttur"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Varsayılan olarak açma ayarları"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Bu uygulama için web bağlantılarının nasıl açılacağını seçin"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Uygulamada"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Tarayıcınızda"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Tamam"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 2006b0ba6929..eca6801a6af3 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"بڑا کریں"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"دائیں منتقل کریں"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"بائیں منتقل کریں"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"بطور ڈیفالٹ ترتیبات کھولیں"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"اس ایپ کے لیے ویب لنکس کھولنے کا طریقہ منتخب کریں"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ایپ میں"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"آپ کے براؤزر میں"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ٹھیک ہے"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index db86498130d8..e381c9860738 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Phóng to tối đa"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Di chuyển nhanh sang trái"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Di chuyển nhanh sang phải"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Mở các chế độ cài đặt theo mặc định"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Chọn cách mở đường liên kết trang web cho ứng dụng này"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Trong ứng dụng"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Trên trình duyệt"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index ebf4b038b098..e39a64df95fc 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"贴靠左侧"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"贴靠右侧"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"默认打开设置"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"选择如何打开此应用中的网页链接"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在此应用内"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在浏览器中"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"确定"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index f1d12fce0103..6cd2567bda45 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"貼齊左邊"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"貼齊右邊"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"採用預設設定打開"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"選擇此應用程式開啟網絡連結的方式"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在應用程式內"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在瀏覽器中"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index b5c28d347acb..c14f66449a0e 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"最大化"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"靠左對齊"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"靠右對齊"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"開啟連結的預設設定"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"選擇如何開啟這個應用程式的網頁連結"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"使用應用程式"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"使用瀏覽器"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index f54d0ed01ea8..70a35422982a 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -136,14 +136,9 @@ <string name="desktop_mode_maximize_menu_maximize_button_text" msgid="3090199175564175845">"Khulisa"</string> <string name="desktop_mode_maximize_menu_snap_left_button_text" msgid="8077452201179893424">"Chofoza kwesobunxele"</string> <string name="desktop_mode_maximize_menu_snap_right_button_text" msgid="7117751068945657304">"Chofoza kwesokudla"</string> - <!-- no translation found for open_by_default_settings_text (2526548548598185500) --> - <skip /> - <!-- no translation found for open_by_default_dialog_subheader_text (1729599730664063881) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_app_text (6978022419634199806) --> - <skip /> - <!-- no translation found for open_by_default_dialog_in_browser_text (8042769465958497081) --> - <skip /> - <!-- no translation found for open_by_default_dialog_dismiss_button_text (3487238795534582291) --> - <skip /> + <string name="open_by_default_settings_text" msgid="2526548548598185500">"Vula amasethingi ngokuzenzakalela"</string> + <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"Khetha indlela yokuvula amalinki ewebhu ale app"</string> + <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ku-app"</string> + <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kubhrawuza yakho"</string> + <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"KULUNGILE"</string> </resources> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java index 7f1e4a873f64..5a2a723cacee 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java @@ -61,11 +61,30 @@ public class SplitScreenConstants { @IntDef(prefix = {"SPLIT_POSITION_"}, value = { SPLIT_POSITION_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT, - SPLIT_POSITION_BOTTOM_OR_RIGHT + SPLIT_POSITION_BOTTOM_OR_RIGHT, }) public @interface SplitPosition { } + // These SPLIT_INDEX constants will be used in the same way as the above SPLIT_POSITION ints, + // but scalable to n apps. Eventually, SPLIT_POSITION can be deprecated and only the below + // will be used. + public static final int SPLIT_INDEX_UNDEFINED = -1; + public static final int SPLIT_INDEX_0 = 0; + public static final int SPLIT_INDEX_1 = 1; + public static final int SPLIT_INDEX_2 = 2; + public static final int SPLIT_INDEX_3 = 3; + + @IntDef(prefix = {"SPLIT_INDEX_"}, value = { + SPLIT_INDEX_UNDEFINED, + SPLIT_INDEX_0, + SPLIT_INDEX_1, + SPLIT_INDEX_2, + SPLIT_INDEX_3 + }) + public @interface SplitIndex { + } + /** * A snap target for two apps, where the split is 33-66. With FLAG_ENABLE_FLEXIBLE_SPLIT, * only used on tablets. @@ -170,7 +189,7 @@ public class SplitScreenConstants { SNAP_TO_NONE, SNAP_TO_START_AND_DISMISS, SNAP_TO_END_AND_DISMISS, - SNAP_TO_MINIMIZE + SNAP_TO_MINIMIZE, }) public @interface SnapPosition {} 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 b90e6e2fab8a..19b51f143241 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 @@ -355,11 +355,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont int keyAction, @BackEvent.SwipeEdge int swipeEdge ) { - mShellExecutor.execute(() -> onMotionEvent( - /* touchX = */ touchX, - /* touchY = */ touchY, - /* keyAction = */ keyAction, - /* swipeEdge = */ swipeEdge)); + mShellExecutor.execute( + () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index a8a8c2a80974..eb9cdab6e856 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1246,9 +1246,12 @@ public class BubbleController implements ConfigurationChangeListener, */ public void dragBubbleToDismiss(String bubbleKey, long timestamp) { String selectedBubbleKey = mBubbleData.getSelectedBubbleKey(); - if (mBubbleData.hasAnyBubbleWithKey(bubbleKey)) { + Bubble bubbleToDismiss = mBubbleData.getAnyBubbleWithkey(bubbleKey); + if (bubbleToDismiss != null) { mBubbleData.dismissBubbleWithKey( bubbleKey, Bubbles.DISMISS_USER_GESTURE_FROM_LAUNCHER, timestamp); + mLogger.log(bubbleToDismiss, + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE); } if (mBubbleData.hasBubbles()) { // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded @@ -1979,11 +1982,15 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void addBubble(Bubble addedBubble) { + // Only log metrics event + mLogger.log(addedBubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED); // Nothing to do for adds, these are handled by launcher / in the bubble bar. } @Override public void updateBubble(Bubble updatedBubble) { + // Only log metrics event + mLogger.log(updatedBubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED); // Nothing to do for updates, these are handled by launcher / in the bubble bar. } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java index 1abe11998500..6d757d26a9bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java @@ -16,7 +16,6 @@ package com.android.wm.shell.bubbles; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.FrameworkStatsLog; @@ -33,7 +32,6 @@ public class BubbleLogger { /** * Bubble UI event. */ - @VisibleForTesting public enum Event implements UiEventLogger.UiEventEnum { // region bubble events diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java index 9f100facc163..85921703d559 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java @@ -19,9 +19,11 @@ package com.android.wm.shell.common.split; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_66_33; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_MINIMIZE; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE; @@ -33,6 +35,9 @@ import android.graphics.Rect; import androidx.annotation.Nullable; +import com.android.wm.shell.Flags; +import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; + import java.util.ArrayList; /** @@ -79,8 +84,10 @@ public class DividerSnapAlgorithm { private final int mTaskHeightInMinimizedMode; private final float mFixedRatio; /** Allows split ratios to calculated dynamically instead of using {@link #mFixedRatio}. */ - private final boolean mAllowFlexibleSplitRatios; - private final boolean mIsHorizontalDivision; + private final boolean mCalculateRatiosBasedOnAvailableSpace; + /** Allows split ratios that go offscreen (a.k.a. "flexible split") */ + private final boolean mAllowOffscreenRatios; + private final boolean mIsLeftRightSplit; /** The first target which is still splitting the screen */ private final SnapTarget mFirstSplitTarget; @@ -94,13 +101,13 @@ public class DividerSnapAlgorithm { public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, int dockSide) { - this(res, displayWidth, displayHeight, dividerSize, isHorizontalDivision, insets, - dockSide, false /* minimized */, true /* resizable */); + boolean isLeftRightSplit, Rect insets, int dockSide) { + this(res, displayWidth, displayHeight, dividerSize, isLeftRightSplit, insets, + dockSide, false /* minimized */, true /* resizable */); } public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, - boolean isHorizontalDivision, Rect insets, int dockSide, boolean isMinimizedMode, + boolean isLeftRightSplit, Rect insets, int dockSide, boolean isMinimizedMode, boolean isHomeResizable) { mMinFlingVelocityPxPerSecond = MIN_FLING_VELOCITY_DP_PER_SECOND * res.getDisplayMetrics().density; @@ -109,7 +116,7 @@ public class DividerSnapAlgorithm { mDividerSize = dividerSize; mDisplayWidth = displayWidth; mDisplayHeight = displayHeight; - mIsHorizontalDivision = isHorizontalDivision; + mIsLeftRightSplit = isLeftRightSplit; mInsets.set(insets); mSnapMode = isMinimizedMode ? SNAP_MODE_MINIMIZED : res.getInteger(com.android.internal.R.integer.config_dockedStackDividerSnapMode); @@ -119,11 +126,14 @@ public class DividerSnapAlgorithm { com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1); mMinimalSizeResizableTask = res.getDimensionPixelSize( com.android.internal.R.dimen.default_minimal_size_resizable_task); - mAllowFlexibleSplitRatios = res.getBoolean( + mCalculateRatiosBasedOnAvailableSpace = res.getBoolean( com.android.internal.R.bool.config_flexibleSplitRatios); + // If this is a small screen or a foldable, use offscreen ratios + mAllowOffscreenRatios = + Flags.enableFlexibleTwoAppSplit() && SplitScreenUtils.allowOffscreenRatios(res); mTaskHeightInMinimizedMode = isHomeResizable ? res.getDimensionPixelSize( com.android.internal.R.dimen.task_height_of_minimized_mode) : 0; - calculateTargets(isHorizontalDivision, dockSide); + calculateTargets(isLeftRightSplit, dockSide); mFirstSplitTarget = mTargets.get(1); mLastSplitTarget = mTargets.get(mTargets.size() - 2); mDismissStartTarget = mTargets.get(0); @@ -208,18 +218,18 @@ public class DividerSnapAlgorithm { } private int getStartInset() { - if (mIsHorizontalDivision) { - return mInsets.top; - } else { + if (mIsLeftRightSplit) { return mInsets.left; + } else { + return mInsets.top; } } private int getEndInset() { - if (mIsHorizontalDivision) { - return mInsets.bottom; - } else { + if (mIsLeftRightSplit) { return mInsets.right; + } else { + return mInsets.bottom; } } @@ -233,6 +243,11 @@ public class DividerSnapAlgorithm { return mFirstSplitTarget.position < position && position < mLastSplitTarget.position; } + /** Returns if we are currently on a device/screen that supports split apps going offscreen. */ + public boolean areOffscreenRatiosSupported() { + return mAllowOffscreenRatios; + } + private SnapTarget snap(int position, boolean hardDismiss) { if (shouldApplyFreeSnapMode(position)) { return new SnapTarget(position, SNAP_TO_NONE); @@ -254,11 +269,11 @@ public class DividerSnapAlgorithm { return mTargets.get(minIndex); } - private void calculateTargets(boolean isHorizontalDivision, int dockedSide) { + private void calculateTargets(boolean isLeftRightSplit, int dockedSide) { mTargets.clear(); - int dividerMax = isHorizontalDivision - ? mDisplayHeight - : mDisplayWidth; + int dividerMax = isLeftRightSplit + ? mDisplayWidth + : mDisplayHeight; int startPos = -mDividerSize; if (dockedSide == DOCKED_RIGHT) { startPos += mInsets.left; @@ -266,57 +281,65 @@ public class DividerSnapAlgorithm { mTargets.add(new SnapTarget(startPos, SNAP_TO_START_AND_DISMISS, 0.35f)); switch (mSnapMode) { case SNAP_MODE_16_9: - addRatio16_9Targets(isHorizontalDivision, dividerMax); + addRatio16_9Targets(isLeftRightSplit, dividerMax); break; case SNAP_FIXED_RATIO: - addFixedDivisionTargets(isHorizontalDivision, dividerMax); + addFixedDivisionTargets(isLeftRightSplit, dividerMax); break; case SNAP_ONLY_1_1: - addMiddleTarget(isHorizontalDivision); + addMiddleTarget(isLeftRightSplit); break; case SNAP_MODE_MINIMIZED: - addMinimizedTarget(isHorizontalDivision, dockedSide); + addMinimizedTarget(isLeftRightSplit, dockedSide); break; } mTargets.add(new SnapTarget(dividerMax, SNAP_TO_END_AND_DISMISS, 0.35f)); } - private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, + private void addNonDismissingTargets(boolean isLeftRightSplit, int topPosition, int bottomPosition, int dividerMax) { - maybeAddTarget(topPosition, topPosition - getStartInset(), SNAP_TO_2_33_66); - addMiddleTarget(isHorizontalDivision); + @PersistentSnapPosition int firstTarget = + mAllowOffscreenRatios ? SNAP_TO_2_10_90 : SNAP_TO_2_33_66; + @PersistentSnapPosition int lastTarget = + mAllowOffscreenRatios ? SNAP_TO_2_90_10 : SNAP_TO_2_66_33; + maybeAddTarget(topPosition, topPosition - getStartInset(), firstTarget); + addMiddleTarget(isLeftRightSplit); maybeAddTarget(bottomPosition, - dividerMax - getEndInset() - (bottomPosition + mDividerSize), SNAP_TO_2_66_33); + dividerMax - getEndInset() - (bottomPosition + mDividerSize), lastTarget); } - private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { - int start = isHorizontalDivision ? mInsets.top : mInsets.left; - int end = isHorizontalDivision - ? mDisplayHeight - mInsets.bottom - : mDisplayWidth - mInsets.right; + private void addFixedDivisionTargets(boolean isLeftRightSplit, int dividerMax) { + int start = isLeftRightSplit ? mInsets.left : mInsets.top; + int end = isLeftRightSplit + ? mDisplayWidth - mInsets.right + : mDisplayHeight - mInsets.bottom; int size = (int) (mFixedRatio * (end - start)) - mDividerSize / 2; - if (mAllowFlexibleSplitRatios) { + + if (mAllowOffscreenRatios) { + // TODO (b/349828130): This is a placeholder value, real measurements to come + size = (int) (0.3f * (end - start)) - mDividerSize / 2; + } else if (mCalculateRatiosBasedOnAvailableSpace) { size = Math.max(size, mMinimalSizeResizableTask); } int topPosition = start + size; int bottomPosition = end - size - mDividerSize; - addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); + addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); } - private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) { - int start = isHorizontalDivision ? mInsets.top : mInsets.left; - int end = isHorizontalDivision - ? mDisplayHeight - mInsets.bottom - : mDisplayWidth - mInsets.right; - int startOther = isHorizontalDivision ? mInsets.left : mInsets.top; - int endOther = isHorizontalDivision + private void addRatio16_9Targets(boolean isLeftRightSplit, int dividerMax) { + int start = isLeftRightSplit ? mInsets.left : mInsets.top; + int end = isLeftRightSplit ? mDisplayWidth - mInsets.right : mDisplayHeight - mInsets.bottom; + int startOther = isLeftRightSplit ? mInsets.top : mInsets.left; + int endOther = isLeftRightSplit + ? mDisplayHeight - mInsets.bottom + : mDisplayWidth - mInsets.right; float size = 9.0f / 16.0f * (endOther - startOther); int sizeInt = (int) Math.floor(size); int topPosition = start + sizeInt; int bottomPosition = end - sizeInt - mDividerSize; - addNonDismissingTargets(isHorizontalDivision, topPosition, bottomPosition, dividerMax); + addNonDismissingTargets(isLeftRightSplit, topPosition, bottomPosition, dividerMax); } /** @@ -324,22 +347,22 @@ public class DividerSnapAlgorithm { * meets the minimal size requirement. */ private void maybeAddTarget(int position, int smallerSize, @SnapPosition int snapPosition) { - if (smallerSize >= mMinimalSizeResizableTask) { + if (smallerSize >= mMinimalSizeResizableTask || mAllowOffscreenRatios) { mTargets.add(new SnapTarget(position, snapPosition)); } } - private void addMiddleTarget(boolean isHorizontalDivision) { - int position = DockedDividerUtils.calculateMiddlePosition(isHorizontalDivision, + private void addMiddleTarget(boolean isLeftRightSplit) { + int position = DockedDividerUtils.calculateMiddlePosition(isLeftRightSplit, mInsets, mDisplayWidth, mDisplayHeight, mDividerSize); mTargets.add(new SnapTarget(position, SNAP_TO_2_50_50)); } - private void addMinimizedTarget(boolean isHorizontalDivision, int dockedSide) { + private void addMinimizedTarget(boolean isLeftRightSplit, int dockedSide) { // In portrait offset the position by the statusbar height, in landscape add the statusbar // height as well to match portrait offset int position = mTaskHeightInMinimizedMode + mInsets.top; - if (!isHorizontalDivision) { + if (isLeftRightSplit) { if (dockedSide == DOCKED_LEFT) { position += mInsets.left; } else if (dockedSide == DOCKED_RIGHT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java index f25dfeafb32c..25157c05d0de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DockedDividerUtils.java @@ -97,12 +97,12 @@ public class DockedDividerUtils { } } - public static int calculateMiddlePosition(boolean isHorizontalDivision, Rect insets, + public static int calculateMiddlePosition(boolean isLeftRightSplit, Rect insets, int displayWidth, int displayHeight, int dividerSize) { - int start = isHorizontalDivision ? insets.top : insets.left; - int end = isHorizontalDivision - ? displayHeight - insets.bottom - : displayWidth - insets.right; + int start = isLeftRightSplit ? insets.left : insets.top; + int end = isLeftRightSplit + ? displayWidth - insets.right + : displayHeight - insets.bottom; return start + (end - start) / 2 - dividerSize / 2; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 83ffaf473999..f73065ea8eb8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -30,6 +30,8 @@ import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLAT import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED; import static com.android.wm.shell.shared.animation.Interpolators.LINEAR; import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_END_AND_DISMISS; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_START_AND_DISMISS; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -438,12 +440,31 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange dividerBounds.right = dividerBounds.left + mDividerWindowWidth; bounds1.right = position; bounds2.left = bounds1.right + mDividerSize; + + // For flexible split, expand app offscreen as well + if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) { + if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) { + bounds1.left = bounds1.right - bounds2.width(); + } else { + bounds2.right = bounds2.left + bounds1.width(); + } + } + } else { position += mRootBounds.top; dividerBounds.top = position - mDividerInsets; dividerBounds.bottom = dividerBounds.top + mDividerWindowWidth; bounds1.bottom = position; bounds2.top = bounds1.bottom + mDividerSize; + + // For flexible split, expand app offscreen as well + if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) { + if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) { + bounds1.top = bounds1.bottom - bounds2.width(); + } else { + bounds2.bottom = bounds2.top + bounds1.width(); + } + } } DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */); DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */); @@ -644,7 +665,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange rootBounds.width(), rootBounds.height(), mDividerSize, - !mIsLeftRightSplit, + mIsLeftRightSplit, insets, mIsLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP /* dockSide */); } @@ -669,6 +690,21 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange }); } + /** + * Moves the divider to the other side of the screen. Does nothing if the divider is in the + * center. + * TODO (b/349828130): Currently only supports the two-app case. For n-apps, + * DividerSnapAlgorithm will need to be refactored, and this function will change as well. + */ + public void flingDividerToOtherSide(@PersistentSnapPosition int currentSnapPosition) { + switch (currentSnapPosition) { + case SNAP_TO_2_10_90 -> + snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getLastSplitTarget()); + case SNAP_TO_2_90_10 -> + snapToTarget(mDividerPosition, mDividerSnapAlgorithm.getFirstSplitTarget()); + } + } + @VisibleForTesting void flingDividerPosition(int from, int to, int duration, @Nullable Runnable flingFinishedCallback) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java index bdbcb4635ae8..65bf389f3819 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java @@ -18,6 +18,10 @@ package com.android.wm.shell.common.split; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_45_45_10; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -38,6 +42,8 @@ import com.android.wm.shell.shared.split.SplitScreenConstants; /** Helper utility class for split screen components to use. */ public class SplitScreenUtils { + private static final int LARGE_SCREEN_MIN_EDGE_DP = 600; + /** Reverse the split position. */ @SplitScreenConstants.SplitPosition public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) { @@ -110,10 +116,9 @@ public class SplitScreenUtils { Configuration config) { // Compare the max bounds sizes as on near-square devices, the insets may result in a // configuration in the other orientation - final boolean isLargeScreen = config.smallestScreenWidthDp >= 600; final Rect maxBounds = config.windowConfiguration.getMaxBounds(); final boolean isLandscape = maxBounds.width() >= maxBounds.height(); - return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen, isLandscape); + return isLeftRightSplit(allowLeftRightSplitInPortrait, isLargeScreen(config), isLandscape); } /** @@ -128,4 +133,41 @@ public class SplitScreenUtils { return isLandscape; } } + + /** + * Returns whether the current config is a large screen (tablet or unfolded foldable) + */ + public static boolean isLargeScreen(Configuration config) { + return config.smallestScreenWidthDp >= LARGE_SCREEN_MIN_EDGE_DP; + } + + /** + * Returns whether we should allow split ratios to go offscreen or not. If the device is a phone + * or a foldable (either screen), we allow it. + */ + public static boolean allowOffscreenRatios(Resources res) { + return !isLargeScreen(res.getConfiguration()) + || + res.getIntArray(com.android.internal.R.array.config_foldedDeviceStates).length != 0; + } + + /** + * Within a particular split layout, we label the stages numerically: 0, 1, 2... from left to + * right (or top to bottom). This function takes in a stage index (0th, 1st, 2nd...) and a + * PersistentSnapPosition and returns if that particular stage is offscreen in that layout. + */ + public static boolean isPartiallyOffscreen(int stageIndex, + @SplitScreenConstants.PersistentSnapPosition int snapPosition) { + switch(snapPosition) { + case SNAP_TO_2_10_90: + case SNAP_TO_3_10_45_45: + return stageIndex == 0; + case SNAP_TO_2_90_10: + return stageIndex == 1; + case SNAP_TO_3_45_45_10: + return stageIndex == 2; + default: + return false; + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index 379e052e7b38..8ebe503a3816 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -21,17 +21,39 @@ import com.android.internal.protolog.ProtoLog import com.android.internal.util.FrameworkStatsLog import com.android.window.flags.Flags import com.android.wm.shell.EventLogTags -import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import java.security.SecureRandom +import java.util.Random +import java.util.concurrent.atomic.AtomicInteger + /** Event logger for logging desktop mode session events */ class DesktopModeEventLogger { + private val random: Random = SecureRandom() + + /** The session id for the current desktop mode session */ + @VisibleForTesting + val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID) + + private fun generateSessionId() = 1 + random.nextInt(1 shl 20) + /** - * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for - * entering desktop mode + * Logs enter into desktop mode with [enterReason] */ - fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { + fun logSessionEnter(enterReason: EnterReason) { + val sessionId = generateSessionId() + val previousSessionId = currentSessionId.getAndSet(sessionId) + if (previousSessionId != NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Existing desktop mode session id: %s found on desktop " + + "mode enter", + previousSessionId + ) + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session enter, session: %s reason: %s", sessionId, enterReason.name @@ -47,12 +69,20 @@ class DesktopModeEventLogger { } /** - * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for - * exiting desktop mode + * Logs exit from desktop mode session with [exitReason] */ - fun logSessionExit(sessionId: Int, exitReason: ExitReason) { + fun logSessionExit(exitReason: ExitReason) { + val sessionId = currentSessionId.getAndSet(NO_SESSION_ID) + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging exit from desktop mode" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging session exit, session: %s reason: %s", sessionId, exitReason.name @@ -68,12 +98,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] was added in the desktop mode session having - * session id [sessionId] + * Logs that a task with [taskUpdate] was added in a desktop mode session */ - fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskAdded(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task added" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task added, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -85,12 +123,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] was removed in the desktop mode session having - * session id [sessionId] + * Logs that a task with [taskUpdate] was removed from a desktop mode session */ - fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskRemoved(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task removed" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task remove, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -102,12 +148,20 @@ class DesktopModeEventLogger { } /** - * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session - * having session id [sessionId] + * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session */ - fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { + fun logTaskInfoChanged(taskUpdate: TaskUpdate) { + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging task info changed" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", sessionId, taskUpdate.instanceId @@ -119,14 +173,23 @@ class DesktopModeEventLogger { } /** - * Logs that a task resize event is starting with [taskSizeUpdate] within a - * Desktop mode [sessionId]. + * Logs that a task resize event is starting with [taskSizeUpdate] within a Desktop mode + * session. */ - fun logTaskResizingStarted(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingStarted(taskSizeUpdate: TaskSizeUpdate) { if (!Flags.enableResizingMetrics()) return + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging start of task resizing" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task resize is starting, session: %s taskId: %s", sessionId, taskSizeUpdate.instanceId @@ -138,14 +201,22 @@ class DesktopModeEventLogger { } /** - * Logs that a task resize event is ending with [taskSizeUpdate] within a - * Desktop mode [sessionId]. + * Logs that a task resize event is ending with [taskSizeUpdate] within a Desktop mode session. */ - fun logTaskResizingEnded(sessionId: Int, taskSizeUpdate: TaskSizeUpdate) { + fun logTaskResizingEnded(taskSizeUpdate: TaskSizeUpdate) { if (!Flags.enableResizingMetrics()) return + val sessionId = currentSessionId.get() + if (sessionId == NO_SESSION_ID) { + ProtoLog.w( + WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: No session id found for logging end of task resizing" + ) + return + } + ProtoLog.v( - ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + WM_SHELL_DESKTOP_MODE, "DesktopModeLogger: Logging task resize is ending, session: %s taskId: %s", sessionId, taskSizeUpdate.instanceId @@ -248,6 +319,7 @@ class DesktopModeEventLogger { } companion object { + /** * Describes a task position and dimensions. * @@ -465,5 +537,6 @@ class DesktopModeEventLogger { FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE private const val DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED + @VisibleForTesting const val NO_SESSION_ID = 0 } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index f847aa8918c2..ed03982d191d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -35,8 +35,6 @@ import androidx.core.util.isEmpty import androidx.core.util.isNotEmpty import androidx.core.util.plus import androidx.core.util.putAll -import com.android.internal.logging.InstanceId -import com.android.internal.logging.InstanceIdSequence import com.android.internal.protolog.ProtoLog import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason @@ -48,8 +46,8 @@ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_ import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE -import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions @@ -65,8 +63,6 @@ class DesktopModeLoggerTransitionObserver( private val desktopModeEventLogger: DesktopModeEventLogger ) : Transitions.TransitionObserver { - private val idSequence: InstanceIdSequence by lazy { InstanceIdSequence(Int.MAX_VALUE) } - init { if (DesktopModeStatus.canEnterDesktopMode(context)) { shellInit.addInitCallback(this::onInit, this) @@ -87,15 +83,7 @@ class DesktopModeLoggerTransitionObserver( // following enter reason could be Screen On private var wasPreviousTransitionExitByScreenOff: Boolean = false - // The instanceId for the current logging session - private var loggerInstanceId: InstanceId? = null - - private val isSessionActive: Boolean - get() = loggerInstanceId != null - - private fun setSessionInactive() { - loggerInstanceId = null - } + @VisibleForTesting var isSessionActive: Boolean = false fun onInit() { transitions.registerObserver(this) @@ -247,38 +235,32 @@ class DesktopModeLoggerTransitionObserver( ) { // Sessions is finishing, log task updates followed by an exit event identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) desktopModeEventLogger.logSessionExit( - loggerInstanceId!!.id, getExitReason(transitionInfo) ) - - setSessionInactive() + isSessionActive = false } else if ( postTransitionVisibleFreeformTasks.isNotEmpty() && preTransitionVisibleFreeformTasks.isEmpty() && !isSessionActive ) { // Session is starting, log enter event followed by task updates - loggerInstanceId = idSequence.newInstanceId() + isSessionActive = true desktopModeEventLogger.logSessionEnter( - loggerInstanceId!!.id, getEnterReason(transitionInfo) ) identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) } else if (isSessionActive) { // Session is neither starting, nor finishing, log task updates if there are any identifyAndLogTaskUpdates( - loggerInstanceId!!.id, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks ) @@ -291,7 +273,6 @@ class DesktopModeLoggerTransitionObserver( /** Compare the old and new state of taskInfos and identify and log the changes */ private fun identifyAndLogTaskUpdates( - sessionId: Int, preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, postTransitionVisibleFreeformTasks: SparseArray<TaskInfo> ) { @@ -302,7 +283,7 @@ class DesktopModeLoggerTransitionObserver( when { // new tasks added previousTaskInfo == null -> { - desktopModeEventLogger.logTaskAdded(sessionId, currentTaskUpdate) + desktopModeEventLogger.logTaskAdded(currentTaskUpdate) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, VISIBLE_TASKS_COUNTER_NAME, @@ -315,14 +296,14 @@ class DesktopModeLoggerTransitionObserver( // TODO(b/347935387): Log changes only once they are stable. buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size()) != currentTaskUpdate -> - desktopModeEventLogger.logTaskInfoChanged(sessionId, currentTaskUpdate) + desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate) } } // find old tasks that were removed preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo -> if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) { - desktopModeEventLogger.logTaskRemoved(sessionId, + desktopModeEventLogger.logTaskRemoved( buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())) Trace.setCounter( Trace.TRACE_TAG_WINDOW_MANAGER, @@ -417,13 +398,6 @@ class DesktopModeLoggerTransitionObserver( visibleFreeformTaskInfos.set(taskInfo.taskId, taskInfo) } - @VisibleForTesting fun getLoggerSessionId(): Int? = loggerInstanceId?.id - - @VisibleForTesting - fun setLoggerSessionId(id: Int) { - loggerInstanceId = InstanceId.fakeInstanceId(id) - } - private fun TransitionInfo.Change.requireTaskInfo(): RunningTaskInfo { return this.taskInfo ?: throw IllegalStateException("Expected TaskInfo in the Change") } 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 52b92a89abdf..61de0777ed05 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 @@ -378,11 +378,12 @@ public class DesktopModeVisualIndicator { private static VisualIndicatorAnimator fadeBoundsIn( @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect startBounds = getIndicatorBounds(displayLayout, type); + final Rect endBounds = getIndicatorBounds(displayLayout, type); + final Rect startBounds = getMinBounds(endBounds); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( - view, startBounds, getMaxBounds(startBounds)); + view, startBounds, endBounds); animator.setInterpolator(new DecelerateInterpolator()); setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM); return animator; @@ -390,8 +391,8 @@ public class DesktopModeVisualIndicator { private static VisualIndicatorAnimator fadeBoundsOut( @NonNull View view, IndicatorType type, @NonNull DisplayLayout displayLayout) { - final Rect endBounds = getIndicatorBounds(displayLayout, type); - final Rect startBounds = getMaxBounds(endBounds); + final Rect startBounds = getIndicatorBounds(displayLayout, type); + final Rect endBounds = getMinBounds(startBounds); view.getBackground().setBounds(startBounds); final VisualIndicatorAnimator animator = new VisualIndicatorAnimator( @@ -422,28 +423,35 @@ public class DesktopModeVisualIndicator { return animator; } + /** Calculates the bounds the indicator should have when fully faded in. */ private static Rect getIndicatorBounds(DisplayLayout layout, IndicatorType type) { - final int padding = layout.stableInsets().top; + final Rect desktopStableBounds = new Rect(); + layout.getStableBounds(desktopStableBounds); + final int padding = desktopStableBounds.top; switch (type) { case TO_FULLSCREEN_INDICATOR: - return new Rect(padding, padding, - layout.width() - padding, - layout.height() - padding); + desktopStableBounds.top += padding; + desktopStableBounds.bottom -= padding; + desktopStableBounds.left += padding; + desktopStableBounds.right -= padding; + return desktopStableBounds; case TO_DESKTOP_INDICATOR: final float adjustmentPercentage = 1f - DesktopTasksController.DESKTOP_MODE_INITIAL_BOUNDS_SCALE; - return new Rect((int) (adjustmentPercentage * layout.width() / 2), - (int) (adjustmentPercentage * layout.height() / 2), - (int) (layout.width() - (adjustmentPercentage * layout.width() / 2)), - (int) (layout.height() - (adjustmentPercentage * layout.height() / 2))); + return new Rect((int) (adjustmentPercentage * desktopStableBounds.width() / 2), + (int) (adjustmentPercentage * desktopStableBounds.height() / 2), + (int) (desktopStableBounds.width() + - (adjustmentPercentage * desktopStableBounds.width() / 2)), + (int) (desktopStableBounds.height() + - (adjustmentPercentage * desktopStableBounds.height() / 2))); case TO_SPLIT_LEFT_INDICATOR: return new Rect(padding, padding, - layout.width() / 2 - padding, - layout.height() - padding); + desktopStableBounds.width() / 2 - padding, + desktopStableBounds.height()); case TO_SPLIT_RIGHT_INDICATOR: - return new Rect(layout.width() / 2 + padding, padding, - layout.width() - padding, - layout.height() - padding); + return new Rect(desktopStableBounds.width() / 2 + padding, padding, + desktopStableBounds.width() - padding, + desktopStableBounds.height()); default: throw new IllegalArgumentException("Invalid indicator type provided."); } @@ -505,17 +513,18 @@ public class DesktopModeVisualIndicator { } /** - * Return the max bounds of a visual indicator + * Return the minimum bounds of a visual indicator, to be used at the end of fading out + * and the start of fading in. */ - private static Rect getMaxBounds(Rect startBounds) { - return new Rect((int) (startBounds.left - - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), - (int) (startBounds.top - - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())), - (int) (startBounds.right - + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())), - (int) (startBounds.bottom - + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height()))); + private static Rect getMinBounds(Rect maxBounds) { + return new Rect((int) (maxBounds.left + + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())), + (int) (maxBounds.top + + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height())), + (int) (maxBounds.right + - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.width())), + (int) (maxBounds.bottom + - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * maxBounds.height()))); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 92535f37094a..b723337cc894 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 @@ -521,7 +521,6 @@ class DesktopTasksController( wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED) transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) - } private fun exitSplitIfApplicable(wct: WindowContainerTransaction, taskInfo: RunningTaskInfo) { @@ -660,10 +659,10 @@ class DesktopTasksController( } val wct = WindowContainerTransaction() + if (!task.isFreeform) addMoveToDesktopChanges(wct, task, displayId) wct.reparent(task.token, displayAreaInfo.token, true /* onTop */) transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) - } /** Moves a task in/out of full immersive state within the desktop. */ @@ -739,7 +738,6 @@ class DesktopTasksController( val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) toggleResizeDesktopTaskTransitionHandler.startTransition(wct) - } private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect { @@ -851,7 +849,6 @@ class DesktopTasksController( val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds) - } @VisibleForTesting @@ -1246,10 +1243,23 @@ class DesktopTasksController( error("Invalid windowing mode: ${callingTask.windowingMode}") } } + val bounds = when (newTaskWindowingMode) { + WINDOWING_MODE_FREEFORM -> { + displayController.getDisplayLayout(callingTask.displayId) + ?.let { getInitialBounds(it, callingTask, callingTask.displayId) } + } + WINDOWING_MODE_MULTI_WINDOW -> { + Rect() + } + else -> { + error("Invalid windowing mode: $newTaskWindowingMode") + } + } return ActivityOptions.makeBasic().apply { launchWindowingMode = newTaskWindowingMode pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS + launchBounds = bounds } } @@ -1302,7 +1312,7 @@ class DesktopTasksController( val displayLayout = displayController.getDisplayLayout(task.displayId) if (displayLayout != null) { val initialBounds = Rect(task.configuration.windowConfiguration.bounds) - cascadeWindow(task, initialBounds, displayLayout) + cascadeWindow(initialBounds, displayLayout, task.displayId) wct.setBounds(task.token, initialBounds) } } @@ -1390,13 +1400,19 @@ class DesktopTasksController( return if (wct.isEmpty) null else wct } + /** + * Apply all changes required when task is first added to desktop. Uses the task's current + * display by default to apply initial bounds and placement relative to the display. + * Use a different [displayId] if the task should be moved to a different display. + */ @VisibleForTesting fun addMoveToDesktopChanges( wct: WindowContainerTransaction, - taskInfo: RunningTaskInfo + taskInfo: RunningTaskInfo, + displayId: Int = taskInfo.displayId, ) { - val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return - val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!! + val displayLayout = displayController.getDisplayLayout(displayId) ?: return + val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)!! val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) { @@ -1405,15 +1421,7 @@ class DesktopTasksController( } else { WINDOWING_MODE_FREEFORM } - val initialBounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) { - calculateInitialBounds(displayLayout, taskInfo) - } else { - getDefaultDesktopTaskBounds(displayLayout) - } - - if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue()) { - cascadeWindow(taskInfo, initialBounds, displayLayout) - } + val initialBounds = getInitialBounds(displayLayout, taskInfo, displayId) if (canChangeTaskPosition(taskInfo)) { wct.setBounds(taskInfo.token, initialBounds) @@ -1425,6 +1433,23 @@ class DesktopTasksController( } } + private fun getInitialBounds( + displayLayout: DisplayLayout, + taskInfo: RunningTaskInfo, + displayId: Int, + ): Rect { + val bounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) { + calculateInitialBounds(displayLayout, taskInfo) + } else { + getDefaultDesktopTaskBounds(displayLayout) + } + + if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) { + cascadeWindow(bounds, displayLayout, displayId) + } + return bounds + } + private fun addMoveToFullscreenChanges( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo @@ -1449,11 +1474,11 @@ class DesktopTasksController( } } - private fun cascadeWindow(task: TaskInfo, bounds: Rect, displayLayout: DisplayLayout) { + private fun cascadeWindow(bounds: Rect, displayLayout: DisplayLayout, displayId: Int) { val stableBounds = Rect() displayLayout.getStableBoundsForDesktopMode(stableBounds) - val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(task.displayId) + val activeTasks = taskRepository.getActiveNonMinimizedOrderedTasks(displayId) activeTasks.firstOrNull()?.let { activeTask -> shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let { cascadeWindow(context.resources, stableBounds, @@ -2052,6 +2077,12 @@ class DesktopTasksController( c.removeDesktop(displayId) } } + + override fun moveToExternalDisplay(taskId: Int) { + executeRemoteCallWithTaskPermission(controller, "moveTaskToExternalDisplay") { c -> + c.moveToNextDisplay(taskId) + } + } } private fun logV(msg: String, vararg arguments: Any?) { 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 8e264b2410f7..34c2f1e760a2 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 @@ -648,7 +648,13 @@ sealed class DragToDesktopTransitionHandler( state.startAborted = true // The start-transition (DRAG_HOLD) is aborted, cancel its jank interaction. interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD) - } else if (state.cancelTransitionToken != transition) { + } else if (state.cancelTransitionToken == transition) { + state.draggedTaskChange?.leash?.let { + state.startTransitionFinishTransaction?.show(it) + } + state.startTransitionFinishCb?.onTransitionFinished(null /* wct */) + clearState() + } else { // This transition being aborted is neither the start, nor the cancel transition, so // it must be the finish transition (DRAG_RELEASE); cancel its jank interaction. interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE) @@ -863,7 +869,8 @@ sealed class DragToDesktopTransitionHandler( companion object { /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */ - internal const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index 86351e364cdd..c27813de5358 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -52,4 +52,7 @@ interface IDesktopMode { /** Remove desktop on the given display */ oneway void removeDesktop(int displayId); + + /** Move a task with given `taskId` to external display */ + void moveToExternalDisplay(int taskId); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index dfa24370590a..5c72cb7f71a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; +import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_RIGHT; @@ -43,14 +44,18 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Region; import android.graphics.drawable.Drawable; +import android.util.Log; import android.view.DragEvent; import android.view.SurfaceControl; +import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; +import android.widget.FrameLayout; import android.widget.LinearLayout; import android.window.WindowContainerToken; @@ -67,14 +72,22 @@ import com.android.wm.shell.shared.animation.Interpolators; import com.android.wm.shell.splitscreen.SplitScreenController; import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; /** * Coordinates the visible drop targets for the current drag within a single display. */ public class DragLayout extends LinearLayout - implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider { + implements ViewTreeObserver.OnComputeInternalInsetsListener, DragLayoutProvider, + DragZoneAnimator{ + static final boolean DEBUG_LAYOUT = false; // While dragging the status bar is hidden. private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_ALERTS @@ -108,13 +121,19 @@ public class DragLayout extends LinearLayout // The last position that was handled by the drag layout private final Point mLastPosition = new Point(); + // Used with enableFlexibleSplit() flag + private List<SplitDragPolicy.Target> mTargets; + private Map<SplitDragPolicy.Target, DropZoneView> mTargetDropMap = new HashMap<>(); + private FrameLayout mAnimatingRootLayout; + // Used with enableFlexibleSplit() flag + @SuppressLint("WrongConstant") public DragLayout(Context context, SplitScreenController splitScreenController, IconProvider iconProvider) { super(context); mSplitScreenController = splitScreenController; mIconProvider = iconProvider; - mPolicy = new SplitDragPolicy(context, splitScreenController); + mPolicy = new SplitDragPolicy(context, splitScreenController, this); mStatusBarManager = context.getSystemService(StatusBarManager.class); mLastConfiguration.setTo(context.getResources().getConfiguration()); @@ -211,11 +230,26 @@ public class DragLayout extends LinearLayout boolean isLeftRightSplit = mSplitScreenController != null && mSplitScreenController.isLeftRightSplit(); if (isLeftRightSplit) { - mDropZoneView1.setBottomInset(mInsets.bottom); - mDropZoneView2.setBottomInset(mInsets.bottom); + if (enableFlexibleSplit()) { + mTargetDropMap.values().forEach(dzv -> dzv.setBottomInset(mInsets.bottom)); + } else { + mDropZoneView1.setBottomInset(mInsets.bottom); + mDropZoneView2.setBottomInset(mInsets.bottom); + } } else { - mDropZoneView1.setBottomInset(0); - mDropZoneView2.setBottomInset(mInsets.bottom); + if (enableFlexibleSplit()) { + Collection<DropZoneView> dropViews = mTargetDropMap.values(); + final DropZoneView[] bottomView = {null}; + dropViews.forEach(dropZoneView -> { + bottomView[0] = dropZoneView; + dropZoneView.setBottomInset(0); + }); + // TODO(b/349828130): necessary? maybe with UI polish + // bottomView[0].setBottomInset(mInsets.bottom); + } else { + mDropZoneView1.setBottomInset(0); + mDropZoneView2.setBottomInset(mInsets.bottom); + } } return super.onApplyWindowInsets(insets); } @@ -233,17 +267,31 @@ public class DragLayout extends LinearLayout final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 || (diff & CONFIG_UI_MODE) != 0; if (themeChanged) { - mDropZoneView1.onThemeChange(); - mDropZoneView2.onThemeChange(); + if (enableFlexibleSplit()) { + mTargetDropMap.values().forEach(DropZoneView::onThemeChange); + } else { + mDropZoneView1.onThemeChange(); + mDropZoneView2.onThemeChange(); + } } mLastConfiguration.setTo(newConfig); requestLayout(); } private void updateContainerMarginsForSingleTask() { - mDropZoneView1.setContainerMargin( - mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin); - mDropZoneView2.setContainerMargin(0, 0, 0, 0); + if (enableFlexibleSplit()) { + DropZoneView firstDropZone = mTargetDropMap.values().stream().findFirst().get(); + mTargetDropMap.values().stream() + .filter(dropZoneView -> dropZoneView != firstDropZone) + .forEach(dropZoneView -> dropZoneView.setContainerMargin(0, 0, 0, 0)); + firstDropZone.setContainerMargin( + mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin + ); + } else { + mDropZoneView1.setContainerMargin( + mDisplayMargin, mDisplayMargin, mDisplayMargin, mDisplayMargin); + mDropZoneView2.setContainerMargin(0, 0, 0, 0); + } } private void updateContainerMargins(boolean isLeftRightSplit) { @@ -306,19 +354,35 @@ public class DragLayout extends LinearLayout } } } else { - // We're already in split so get taskInfo from the controller to populate icon / color. - ActivityManager.RunningTaskInfo topOrLeftTask = - mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); - ActivityManager.RunningTaskInfo bottomOrRightTask = - mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); - if (topOrLeftTask != null && bottomOrRightTask != null) { - Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo); - int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask); - Drawable bottomOrRightIcon = mIconProvider.getIcon( - bottomOrRightTask.topActivityInfo); - int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask); - mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon); - mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon); + ActivityManager.RunningTaskInfo[] taskInfos = mSplitScreenController.getAllTaskInfos(); + boolean anyTasksNull = Arrays.stream(taskInfos).anyMatch(Objects::isNull); + if (enableFlexibleSplit() && taskInfos != null && !anyTasksNull) { + int i = 0; + for (DropZoneView v : mTargetDropMap.values()) { + if (i >= taskInfos.length) { + // TODO(b/349828130) Support once we add 3 StageRoots + continue; + } + ActivityManager.RunningTaskInfo task = taskInfos[i]; + v.setAppInfo(getResizingBackgroundColor(task), + mIconProvider.getIcon(task.topActivityInfo)); + i++; + } + } else { + // We're already in split so get taskInfo from the controller to populate icon / color. + ActivityManager.RunningTaskInfo topOrLeftTask = + mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); + ActivityManager.RunningTaskInfo bottomOrRightTask = + mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); + if (topOrLeftTask != null && bottomOrRightTask != null) { + Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo); + int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask); + Drawable bottomOrRightIcon = mIconProvider.getIcon( + bottomOrRightTask.topActivityInfo); + int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask); + mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon); + mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon); + } } // Update the dropzones to match existing split sizes @@ -391,7 +455,14 @@ public class DragLayout extends LinearLayout @NonNull @Override public void addDraggingView(ViewGroup rootView) { - // TODO(b/349828130) We need to separate out view + logic here + if (enableFlexibleSplit()) { + removeAllViews(); + mAnimatingRootLayout = new FrameLayout(getContext()); + addView(mAnimatingRootLayout, + new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + ((LayoutParams) mAnimatingRootLayout.getLayoutParams()).weight = 1; + } + rootView.addView(this, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } @@ -409,6 +480,24 @@ public class DragLayout extends LinearLayout // Inset the draw region by a little bit target.drawRegion.inset(mDisplayMargin, mDisplayMargin); } + + if (enableFlexibleSplit()) { + mTargets = targets; + mTargetDropMap.clear(); + for (int i = 0; i < mTargets.size(); i++) { + DropZoneView v = new DropZoneView(getContext()); + SplitDragPolicy.Target t = mTargets.get(i); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(t.drawRegion.width(), + t.drawRegion.height()); + mAnimatingRootLayout.addView(v, params); + v.setTranslationX(t.drawRegion.left); + v.setTranslationY(t.drawRegion.top); + mTargetDropMap.put(t, v); + if (DEBUG_LAYOUT) { + v.setDebugIndex(t.index); + } + } + } } /** @@ -433,6 +522,9 @@ public class DragLayout extends LinearLayout if (target == null) { // Animating to no target animateSplitContainers(false, null /* animCompleteCallback */); + if (enableFlexibleSplit()) { + animateHighlight(target); + } } else if (mCurrentTarget == null) { if (mPolicy.getNumTargets() == 1) { animateFullscreenContainer(true); @@ -440,10 +532,14 @@ public class DragLayout extends LinearLayout animateSplitContainers(true, null /* animCompleteCallback */); animateHighlight(target); } - } else if (mCurrentTarget.type != target.type) { + } else if (mCurrentTarget.type != target.type || enableFlexibleSplit()) { // Switching between targets - mDropZoneView1.animateSwitch(); - mDropZoneView2.animateSwitch(); + if (enableFlexibleSplit()) { + animateHighlight(target); + } else { + mDropZoneView1.animateSwitch(); + mDropZoneView2.animateSwitch(); + } // Announce for accessibility. switch (target.type) { case TYPE_SPLIT_LEFT: @@ -490,6 +586,9 @@ public class DragLayout extends LinearLayout mDropZoneView2.setForceIgnoreBottomMargin(false); updateContainerMargins(mIsLeftRightSplit); mCurrentTarget = null; + if (enableFlexibleSplit()) { + mAnimatingRootLayout.removeAllViews(); + } } /** @@ -566,9 +665,20 @@ public class DragLayout extends LinearLayout mStatusBarManager.disable(visible ? HIDE_STATUS_BAR_FLAGS : DISABLE_NONE); - mDropZoneView1.setShowingMargin(visible); - mDropZoneView2.setShowingMargin(visible); - Animator animator = mDropZoneView1.getAnimator(); + Animator animator; + if (enableFlexibleSplit()) { + DropZoneView anyDropZoneView = null; + for (DropZoneView dz : mTargetDropMap.values()) { + dz.setShowingMargin(visible); + anyDropZoneView = dz; + } + animator = anyDropZoneView != null ? anyDropZoneView.getAnimator() : null; + } else { + mDropZoneView1.setShowingMargin(visible); + mDropZoneView2.setShowingMargin(visible); + animator = mDropZoneView1.getAnimator(); + } + if (animCompleteCallback != null) { if (animator != null) { animator.addListener(new AnimatorListenerAdapter() { @@ -584,7 +694,24 @@ public class DragLayout extends LinearLayout } } + @Override + public void animateDragTargets( + @NonNull List<? extends BiConsumer<SplitDragPolicy.Target, View>> viewsToAnimate) { + for (Map.Entry<SplitDragPolicy.Target, DropZoneView> entry : mTargetDropMap.entrySet()) { + viewsToAnimate.get(0).accept(entry.getKey(), entry.getValue()); + } + } + private void animateHighlight(SplitDragPolicy.Target target) { + if (enableFlexibleSplit()) { + for (Map.Entry<SplitDragPolicy.Target, DropZoneView> dzv : mTargetDropMap.entrySet()) { + // Highlight the view w/ the matching target, unhighlight the rest + dzv.getValue().setShowingHighlight(dzv.getKey() == target); + } + mPolicy.onHoveringOver(target); + return; + } + if (target.type == TYPE_SPLIT_LEFT || target.type == TYPE_SPLIT_TOP) { mDropZoneView1.setShowingHighlight(true); mDropZoneView2.setShowingHighlight(false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt new file mode 100644 index 000000000000..240465d00d38 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragZoneAnimator.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.draganddrop + +import android.view.View +import java.util.function.BiConsumer + +interface DragZoneAnimator { + /** + * Each consumer will be called for the corresponding DropZoneView. + * This must match the number of targets in [.mTargets] otherwise will + * throw an [IllegalStateException] + */ + fun animateDragTargets(viewsToAnimate: List<BiConsumer<SplitDragPolicy.Target, View>>) +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt index 122a105dbf8d..2bbca489ec36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropTarget.kt @@ -47,7 +47,7 @@ interface DropTarget { /** * Called when user is hovering Drag object over the given Target */ - fun onHoveringOver(target: SplitDragPolicy.Target) {} + fun onHoveringOver(target: SplitDragPolicy.Target?) {} /** * Called when the user has dropped the provided target (need not be the same target as * [onHoveringOver]) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java index f9749ec1e2b7..e503b8c612e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java @@ -20,12 +20,14 @@ import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_ import android.animation.Animator; import android.animation.ObjectAnimator; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Path; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.util.AttributeSet; import android.util.FloatProperty; import android.view.Gravity; @@ -33,6 +35,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.Nullable; @@ -83,6 +86,7 @@ public class DropZoneView extends FrameLayout { private int mTargetBackgroundColor; private ObjectAnimator mMarginAnimator; private float mMarginPercent; + private TextView mDebugIndex; // Renders a highlight or neutral transparent color private ColorDrawable mColorDrawable; @@ -125,6 +129,22 @@ public class DropZoneView extends FrameLayout { mMarginView = new MarginView(context); addView(mMarginView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + if (DEBUG_LAYOUT) { + mDebugIndex = new TextView(context); + mDebugIndex.setVisibility(GONE); + mDebugIndex.setTextColor(Color.YELLOW); + addView(mDebugIndex, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP)); + + View borderView = new View(context); + addView(borderView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + GradientDrawable border = new GradientDrawable(); + border.setShape(GradientDrawable.RECTANGLE); + border.setStroke(5, Color.RED); + borderView.setBackground(border); + } } public void onThemeChange() { @@ -236,6 +256,16 @@ public class DropZoneView extends FrameLayout { } } + @SuppressLint("SetTextI18n") + public void setDebugIndex(int index) { + if (!DEBUG_LAYOUT) { + return; + } + + mDebugIndex.setText("Index:\n" + index); + mDebugIndex.setVisibility(VISIBLE); + } + private void animateBackground(int startColor, int endColor) { if (DEBUG_LAYOUT) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java index 2a19d6512b56..5d22c1edf8fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java @@ -32,16 +32,22 @@ import static android.content.Intent.EXTRA_USER; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static com.android.wm.shell.Flags.enableFlexibleSplit; +import static com.android.wm.shell.draganddrop.DragLayout.DEBUG_LAYOUT; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_FULLSCREEN; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_RIGHT; import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_TOP; import static com.android.wm.shell.shared.draganddrop.DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.PendingIntent; @@ -59,6 +65,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Slog; +import android.view.View; import android.window.WindowContainerToken; import androidx.annotation.IntDef; @@ -69,13 +76,23 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.R; +import com.android.wm.shell.draganddrop.anim.DropTargetAnimSupplier; +import com.android.wm.shell.draganddrop.anim.HoverAnimProps; +import com.android.wm.shell.draganddrop.anim.TwoFiftyFiftyTargetAnimator; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.shared.split.SplitScreenConstants; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreenController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +import kotlin.Pair; /** * The policy for handling drag and drop operations to shell. @@ -89,24 +106,42 @@ public class SplitDragPolicy implements DropTarget { private final Starter mFullscreenStarter; // Used for launching tasks into splitscreen private final Starter mSplitscreenStarter; + private final DragZoneAnimator mDragZoneAnimator; private final SplitScreenController mSplitScreen; - private final ArrayList<SplitDragPolicy.Target> mTargets = new ArrayList<>(); + private ArrayList<SplitDragPolicy.Target> mTargets = new ArrayList<>(); private final RectF mDisallowHitRegion = new RectF(); + /** + * Maps a given SnapPosition to an array where each index of the array represents one + * of the targets that are being hovered over, in order (Left to Right, Top to Bottom). + * Ex: 4 drop targets when we're in 50/50 split + * 2_50_50 => [ [AnimPropsTarget1, AnimPropsTarget2, AnimPropsTarget3, AnimPropsTarget4], + * ... // hovering over target 2, + * ... // hovering over target 3, + * ... // hovering over target 4 + * ] + */ + private final Map<Integer, List<List<HoverAnimProps>>> mHoverAnimProps = new HashMap(); private InstanceId mLoggerSessionId; private DragSession mSession; + @Nullable + private Target mCurrentHoverTarget; + /** This variable is a temporary placeholder, will be queried on drag start. */ + private int mCurrentSnapPosition = -1; - public SplitDragPolicy(Context context, SplitScreenController splitScreen) { - this(context, splitScreen, new DefaultStarter(context)); + public SplitDragPolicy(Context context, SplitScreenController splitScreen, + DragZoneAnimator dragZoneAnimator) { + this(context, splitScreen, new DefaultStarter(context), dragZoneAnimator); } @VisibleForTesting SplitDragPolicy(Context context, SplitScreenController splitScreen, - Starter fullscreenStarter) { + Starter fullscreenStarter, DragZoneAnimator dragZoneAnimator) { mContext = context; mSplitScreen = splitScreen; mFullscreenStarter = fullscreenStarter; mSplitscreenStarter = splitScreen; + mDragZoneAnimator = dragZoneAnimator; } /** @@ -164,58 +199,123 @@ public class SplitDragPolicy implements DropTarget { || (mSession.runningTaskActType == ACTIVITY_TYPE_STANDARD && mSession.runningTaskWinMode == WINDOWING_MODE_FULLSCREEN); if (allowSplit) { - // Already split, allow replacing existing split task - final Rect topOrLeftBounds = new Rect(); - final Rect bottomOrRightBounds = new Rect(); - mSplitScreen.getStageBounds(topOrLeftBounds, bottomOrRightBounds); - topOrLeftBounds.intersect(displayRegion); - bottomOrRightBounds.intersect(displayRegion); - - if (isLeftRightSplit) { - final Rect leftHitRegion = new Rect(); - final Rect rightHitRegion = new Rect(); - - // If we have existing split regions use those bounds, otherwise split it 50/50 - if (inSplitScreen) { - // The bounds of the existing split will have a divider bar, the hit region - // should include that space. Find the center of the divider bar: - float centerX = topOrLeftBounds.right + (dividerWidth / 2); - // Now set the hit regions using that center. - leftHitRegion.set(displayRegion); - leftHitRegion.right = (int) centerX; - rightHitRegion.set(displayRegion); - rightHitRegion.left = (int) centerX; + if (enableFlexibleSplit()) { + // TODO(b/349828130) get this from split screen controller, expose the SnapTarget object + // entirely and then pull out the SnapPosition + @SplitScreenConstants.SnapPosition int snapPosition = SNAP_TO_2_50_50; + final Rect startHitRegion = new Rect(); + final Rect endHitRegion = new Rect(); + if (!inSplitScreen) { + // Currently in fullscreen, split in half + final Rect startBounds = new Rect(); + final Rect endBounds = new Rect(); + mSplitScreen.getStageBounds(startBounds, endBounds); + startBounds.intersect(displayRegion); + endBounds.intersect(displayRegion); + + if (isLeftRightSplit) { + displayRegion.splitVertically(startHitRegion, endHitRegion); + } else { + displayRegion.splitHorizontally(startHitRegion, endHitRegion); + } + + mTargets.add(new Target(TYPE_SPLIT_LEFT, startHitRegion, startBounds, -1)); + mTargets.add(new Target(TYPE_SPLIT_RIGHT, endHitRegion, endBounds, -1)); } else { - displayRegion.splitVertically(leftHitRegion, rightHitRegion); + // TODO(b/349828130), move this into init function and/or the insets updating + // callback + DropTargetAnimSupplier supplier = null; + switch (snapPosition) { + case SNAP_TO_2_50_50: + supplier = new TwoFiftyFiftyTargetAnimator(); + break; + case SplitScreenConstants.SNAP_TO_2_33_66: + break; + case SplitScreenConstants.SNAP_TO_2_66_33: + break; + case SplitScreenConstants.SNAP_TO_END_AND_DISMISS: + break; + case SplitScreenConstants.SNAP_TO_MINIMIZE: + break; + case SplitScreenConstants.SNAP_TO_NONE: + break; + case SplitScreenConstants.SNAP_TO_START_AND_DISMISS: + break; + default: + } + + Pair<List<Target>, List<List<HoverAnimProps>>> targetsAnims = + supplier.getTargets(mSession.displayLayout, + insets, isLeftRightSplit, mContext.getResources()); + mTargets = new ArrayList<>(targetsAnims.getFirst()); + mHoverAnimProps.put(SNAP_TO_2_50_50, targetsAnims.getSecond()); + assert(mTargets.size() == targetsAnims.getSecond().size()); + if (DEBUG_LAYOUT) { + for (List<HoverAnimProps> props : targetsAnims.getSecond()) { + StringBuilder sb = new StringBuilder(); + for (HoverAnimProps hap : props) { + sb.append(hap).append("\n"); + } + sb.append("\n"); + Log.d(TAG, sb.toString()); + } + } } - - mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds)); - mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds)); - } else { - final Rect topHitRegion = new Rect(); - final Rect bottomHitRegion = new Rect(); - - // If we have existing split regions use those bounds, otherwise split it 50/50 - if (inSplitScreen) { - // The bounds of the existing split will have a divider bar, the hit region - // should include that space. Find the center of the divider bar: - float centerX = topOrLeftBounds.bottom + (dividerWidth / 2); - // Now set the hit regions using that center. - topHitRegion.set(displayRegion); - topHitRegion.bottom = (int) centerX; - bottomHitRegion.set(displayRegion); - bottomHitRegion.top = (int) centerX; + // Already split, allow replacing existing split task + final Rect topOrLeftBounds = new Rect(); + final Rect bottomOrRightBounds = new Rect(); + mSplitScreen.getStageBounds(topOrLeftBounds, bottomOrRightBounds); + topOrLeftBounds.intersect(displayRegion); + bottomOrRightBounds.intersect(displayRegion); + + if (isLeftRightSplit) { + final Rect leftHitRegion = new Rect(); + final Rect rightHitRegion = new Rect(); + + // If we have existing split regions use those bounds, otherwise split it 50/50 + if (inSplitScreen) { + // The bounds of the existing split will have a divider bar, the hit region + // should include that space. Find the center of the divider bar: + float centerX = topOrLeftBounds.right + (dividerWidth / 2); + // Now set the hit regions using that center. + leftHitRegion.set(displayRegion); + leftHitRegion.right = (int) centerX; + rightHitRegion.set(displayRegion); + rightHitRegion.left = (int) centerX; + } else { + displayRegion.splitVertically(leftHitRegion, rightHitRegion); + } + + mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds, -1)); + mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds, + -1)); } else { - displayRegion.splitHorizontally(topHitRegion, bottomHitRegion); + final Rect topHitRegion = new Rect(); + final Rect bottomHitRegion = new Rect(); + + // If we have existing split regions use those bounds, otherwise split it 50/50 + if (inSplitScreen) { + // The bounds of the existing split will have a divider bar, the hit region + // should include that space. Find the center of the divider bar: + float centerX = topOrLeftBounds.bottom + (dividerWidth / 2); + // Now set the hit regions using that center. + topHitRegion.set(displayRegion); + topHitRegion.bottom = (int) centerX; + bottomHitRegion.set(displayRegion); + bottomHitRegion.top = (int) centerX; + } else { + displayRegion.splitHorizontally(topHitRegion, bottomHitRegion); + } + + mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds, -1)); + mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds, + -1)); } - - mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds)); - mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds)); } } else { // Split-screen not allowed, so only show the fullscreen target - mTargets.add(new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion)); + mTargets.add(new Target(TYPE_FULLSCREEN, fullscreenHitRegion, fullscreenDrawRegion, -1)); } return mTargets; } @@ -230,6 +330,22 @@ public class SplitDragPolicy implements DropTarget { } for (int i = mTargets.size() - 1; i >= 0; i--) { SplitDragPolicy.Target t = mTargets.get(i); + if (enableFlexibleSplit() && mCurrentHoverTarget != null) { + // If we're in flexible split, the targets themselves animate, so we have to rely + // on the view's animated position for subsequent drag coordinates which we also + // cache in HoverAnimProps. + List<List<HoverAnimProps>> hoverAnimPropTargets = + mHoverAnimProps.get(mCurrentSnapPosition); + for (HoverAnimProps animProps : + hoverAnimPropTargets.get(mCurrentHoverTarget.index)) { + if (animProps.getHoverRect() != null && + animProps.getHoverRect().contains(x, y)) { + return animProps.getTarget(); + } + } + + } + if (t.hitRegion.contains(x, y)) { return t; } @@ -266,6 +382,10 @@ public class SplitDragPolicy implements DropTarget { } else { launchIntent(mSession, starter, position, hideTaskToken); } + + if (enableFlexibleSplit()) { + reset(); + } } /** @@ -335,6 +455,82 @@ public class SplitDragPolicy implements DropTarget { null /* fillIntent */, position, opts, hideTaskToken); } + @Override + public void onHoveringOver(Target hoverTarget) { + final boolean isLeftRightSplit = mSplitScreen != null && mSplitScreen.isLeftRightSplit(); + final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible(); + if (!inSplitScreen) { + // no need to animate for entering 50/50 split + return; + } + + mCurrentHoverTarget = hoverTarget; + if (hoverTarget == null) { + // Reset to default state + BiConsumer<Target, View> biConsumer = new BiConsumer<Target, View>() { + @Override + public void accept(Target target, View view) { + // take into account left/right split + Animator transX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, + target.drawRegion.left); + Animator transY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, + target.drawRegion.top); + Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, 1); + Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, 1); + AnimatorSet as = new AnimatorSet(); + as.play(transX); + as.play(transY); + as.play(scaleX); + as.play(scaleY); + + as.start(); + } + }; + mDragZoneAnimator.animateDragTargets(List.of(biConsumer)); + return; + } + + // TODO(b/349828130) get this from split controller + @SplitScreenConstants.SnapPosition int snapPosition = SNAP_TO_2_50_50; + mCurrentSnapPosition = SNAP_TO_2_50_50; + List<BiConsumer<Target, View>> animatingConsumers = new ArrayList<>(); + final List<List<HoverAnimProps>> hoverAnimProps = mHoverAnimProps.get(snapPosition); + List<HoverAnimProps> animProps = hoverAnimProps.get(hoverTarget.index); + // Expand start and push out the rest to the end + BiConsumer<Target, View> biConsumer = new BiConsumer<>() { + @Override + public void accept(Target target, View view) { + if (animProps.isEmpty() || animProps.size() < (target.index + 1)) { + return; + } + HoverAnimProps singleAnimProp = animProps.get(target.index); + Animator transX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, + singleAnimProp.getTransX()); + Animator transY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, + singleAnimProp.getTransY()); + Animator scaleX = ObjectAnimator.ofFloat(view, View.SCALE_X, + singleAnimProp.getScaleX()); + Animator scaleY = ObjectAnimator.ofFloat(view, View.SCALE_Y, + singleAnimProp.getScaleY()); + AnimatorSet as = new AnimatorSet(); + as.play(transX); + as.play(transY); + as.play(scaleX); + as.play(scaleY); + as.start(); + } + }; + animatingConsumers.add(biConsumer); + mDragZoneAnimator.animateDragTargets(animatingConsumers); + } + + private void reset() { + mCurrentHoverTarget = null; + mCurrentSnapPosition = -1; + } + + + /** * Interface for actually committing the task launches. */ @@ -425,7 +621,7 @@ public class SplitDragPolicy implements DropTarget { */ public static class Target { static final int TYPE_FULLSCREEN = 0; - static final int TYPE_SPLIT_LEFT = 1; + public static final int TYPE_SPLIT_LEFT = 1; static final int TYPE_SPLIT_TOP = 2; static final int TYPE_SPLIT_RIGHT = 3; static final int TYPE_SPLIT_BOTTOM = 4; @@ -445,16 +641,23 @@ public class SplitDragPolicy implements DropTarget { final Rect hitRegion; // The approximate visual region for where the task will start final Rect drawRegion; + int index; - public Target(@Type int t, Rect hit, Rect draw) { + /** + * @param index 0-indexed, represents which position of drop target this object represents, + * 0 to N for left to right, top to bottom + */ + public Target(@Type int t, Rect hit, Rect draw, int index) { type = t; hitRegion = hit; drawRegion = draw; + this.index = index; } @Override public String toString() { - return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion + "}"; + return "Target {type=" + type + " hit=" + hitRegion + " draw=" + drawRegion + + " index=" + index + "}"; } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/DropTargetAnimSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/DropTargetAnimSupplier.kt new file mode 100644 index 000000000000..bb34613ca431 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/DropTargetAnimSupplier.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.draganddrop.anim + +import android.content.res.Resources +import android.graphics.Insets +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.draganddrop.SplitDragPolicy + +/** + * When the user is dragging an icon from Taskbar to add an app into split + * screen, we have a set of rules by which we draw and move colored drop + * targets around the screen. The rules are provided through this interface. + * + * Each possible screen layout should have an implementation of this interface. + * E.g. + * - 50:50 two-app split + * - 10:45:45 three-app split + * - single app, no split + * = three implementations of this interface. + */ +interface DropTargetAnimSupplier { + /** + * Returns a Pair of lists. + * First list (length n): Where to draw the n colored drop zones. + * Second list (length n): How to animate the drop zones as user hovers around. + * + * Ex: First list => [A, B, C] // 3 views will be created representing these 3 targets + * Second list => [ + * [A (scaleX=4), B (translateX=20), C (translateX=20)], // hovering over A + * [A (translateX=20), B (scaleX=4), C (translateX=20)], // hovering over B + * [A (translateX=20), B (translateX=20), C (scaleX=4)], // hovering over C + * ] + * + * All indexes assume 0 to N => left to right when [isLeftRightSplit] is true and top to bottom + * when [isLeftRightSplit] is false. Indexing is left to right even in RtL mode. + * + * All lists should have the SAME number of elements, even if no animations are to be run for + * a given target while in a hover state. + * It's not that we don't trust you, but we _really_ don't trust you, so this will throw an + * exception if lengths are different. Don't ruin it for everyone else... + * or do. Idk, you're an adult. + */ + fun getTargets(displayLayout: DisplayLayout, insets: Insets, isLeftRightSplit: Boolean, + resources: Resources) : + Pair<List<SplitDragPolicy.Target>, List<List<HoverAnimProps>>> +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/HoverAnimProps.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/HoverAnimProps.kt new file mode 100644 index 000000000000..d61caeb8d606 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/HoverAnimProps.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.draganddrop.anim + +import android.graphics.Rect +import com.android.wm.shell.draganddrop.SplitDragPolicy + +/** + * Contains the animation props to represent a single state of drop targets. + * When the user is dragging, we'd be going between different HoverAnimProps + */ +data class HoverAnimProps( + var target: SplitDragPolicy.Target, + val transX: Float, + val transY: Float, + val scaleX: Float, + val scaleY: Float, + /** + * Pass in null to indicate this target cannot be hovered over for this given animation/ + * state + * + * TODO: There's some way we can probably use the existing translation/scaling values + * to take [.target]'s hitRect and scale that so we don't have to take in a separate + * hoverRect in the CTOR. Have to make sure the pivots match since view's pivot in the + * center of the view and rect's pivot at 0, 0 if unspecified. + * The two may also not be correlated, but worth investigating + * + */ + var hoverRect: Rect? +) { + + override fun toString(): String { + return ("targetId: " + target + + " translationX: " + transX + + " translationY: " + transY + + " scaleX: " + scaleX + + " scaleY: " + scaleY + + " hoverRect: " + hoverRect) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt new file mode 100644 index 000000000000..9f532f57961d --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.draganddrop.anim + +import android.content.res.Resources +import android.graphics.Insets +import android.graphics.Rect +import com.android.wm.shell.R +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.draganddrop.SplitDragPolicy.Target + +/** + * Represents Drop Zone targets and animations for when the system is currently in a 2 app 50/50 + * split. + * SnapPosition = 2_50_50 + * + * NOTE: Naming convention for many variables is done as "hXtYZ" + * This means that variable is a transformation on the Z property for target index Y while the user + * is hovering over target index X + * Ex: h1t2scaleX=2 => User is hovering over target index 1, target index 2 should scaleX by 2 + * + * TODO(b/349828130): Everything in this class is temporary, none of this is up to spec. + */ +class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier { + /** + * TODO: Could we transpose all the horizontal rects by 90 degrees and have that suffice for + * top bottom split?? Hmmm... Doubt it. + */ + override fun getTargets( + displayLayout: DisplayLayout, + insets: Insets, + isLeftRightSplit: Boolean, + resources: Resources + ): Pair<List<Target>, List<List<HoverAnimProps>>> { + val targets : ArrayList<Target> = ArrayList() + val w: Int = displayLayout.width() + val h: Int = displayLayout.height() + val iw = w - insets.left - insets.right + val ih = h - insets.top - insets.bottom + val l = insets.left + val t = insets.top + val displayRegion = Rect(l, t, l + iw, t + ih) + val fullscreenDrawRegion = Rect(displayRegion) + val dividerWidth: Float = resources.getDimensionPixelSize( + R.dimen.split_divider_bar_width + ).toFloat() + + val farStartBounds = Rect() + farStartBounds.set(displayRegion) + val startBounds = Rect() + startBounds.set(displayRegion) + val endBounds = Rect() + endBounds.set(displayRegion) + val farEndBounds = Rect() + farEndBounds.set(displayRegion) + val endsPercent = 0.10f + val visibleStagePercent = 0.45f + val halfDividerWidth = dividerWidth.toInt() / 2 + val endsWidth = Math.round(displayRegion.width() * endsPercent) + val stageWidth = Math.round(displayRegion.width() * visibleStagePercent) + + + // Place the farStart and farEnds outside of the display, and then + // animate them in once the hover starts + // | = divider; || = display boundary + // farStart || start | end || farEnd + farStartBounds.left = -endsWidth + farStartBounds.right = 0 + startBounds.left = farStartBounds.right + dividerWidth.toInt() + startBounds.right = startBounds.left + stageWidth + endBounds.left = startBounds.right + dividerWidth.toInt() + endBounds.right = endBounds.left + stageWidth + farEndBounds.left = fullscreenDrawRegion.right + farEndBounds.right = farEndBounds.left + endsWidth + + + // For the hit rect, trim the divider space we've added between the + // rects + targets.add( + Target( + Target.TYPE_SPLIT_LEFT, + Rect( + farStartBounds.left, farStartBounds.top, + farStartBounds.right + halfDividerWidth, + farStartBounds.bottom + ), + farStartBounds, 0 + ) + ) + targets.add( + Target( + Target.TYPE_SPLIT_LEFT, + Rect( + startBounds.left - halfDividerWidth, + startBounds.top, + startBounds.right + halfDividerWidth, + startBounds.bottom + ), + startBounds, 1 + ) + ) + targets.add( + Target( + Target.TYPE_SPLIT_LEFT, + Rect( + endBounds.left - halfDividerWidth, + endBounds.top, endBounds.right, endBounds.bottom + ), + endBounds, 2 + ) + ) + targets.add( + Target( + Target.TYPE_SPLIT_LEFT, + Rect( + farEndBounds.left - halfDividerWidth, + farEndBounds.top, farEndBounds.right, farEndBounds.bottom + ), + farEndBounds, 3 + ) + ) + + + // Hovering over target 0, + // * increase scaleX of target 0 + // * decrease scaleX of target 1, 2 + // * ensure target 3 offscreen + + // bring target 0 in from offscreen and expand + val h0t0ScaleX = stageWidth.toFloat() / endsWidth + val h0t0TransX: Float = stageWidth / h0t0ScaleX + dividerWidth + val h0t0HoverProps = HoverAnimProps( + targets.get(0), + h0t0TransX, farStartBounds.top.toFloat(), h0t0ScaleX, 1f, + Rect( + 0, 0, (stageWidth + dividerWidth).toInt(), + farStartBounds.bottom + ) + ) + + + // move target 1 over to the middle/end + val h0t1TransX = stageWidth.toFloat() + val h0t1ScaleX = 1f + val h0t1HoverProps = HoverAnimProps( + targets.get(1), + h0t1TransX, startBounds.top.toFloat(), h0t1ScaleX, 1f, + Rect( + stageWidth, 0, (stageWidth + h0t1TransX).toInt(), + farStartBounds.bottom + ) + ) + + + // move target 2 to the very end + val h0t2TransX = endBounds.left + stageWidth / 2f + val h0t2ScaleX = endsWidth.toFloat() / stageWidth + val h0t2HoverProps = HoverAnimProps( + targets.get(2), + h0t2TransX, endBounds.top.toFloat(), h0t2ScaleX, 1f, + Rect( + displayRegion.right as Int - endsWidth, 0, + displayRegion.right as Int, + farStartBounds.bottom + ) + ) + + + // move target 3 off-screen + val h0t3TransX = farEndBounds.right.toFloat() + val h0t3ScaleX = 1f + val h0t3HoverProps = HoverAnimProps( + targets.get(3), + h0t3TransX, farEndBounds.top.toFloat(), h0t3ScaleX, 1f, + null + ) + val animPropsForHoverTarget0 = + listOf(h0t0HoverProps, h0t1HoverProps, h0t2HoverProps, h0t3HoverProps) + + + // Hovering over target 1, + // * Bring in target 0 from offscreen start + // * Shift over target 1 + // * Slightly lower scale of target 2 + // * Ensure target 4 offscreen + // bring target 0 in from offscreen + val h1t0TransX = 0f + val h1t0ScaleX = 1f + val h1t0HoverProps = HoverAnimProps( + targets.get(0), + h1t0TransX, farStartBounds.top.toFloat(), h1t0ScaleX, 1f, + Rect( + 0, 0, (farStartBounds.width() + dividerWidth).toInt(), + farStartBounds.bottom + ) + ) + + + // move target 1 over a tiny bit by same amount and make it smaller + val h1t1TransX: Float = endsWidth + dividerWidth + val h1t1ScaleX = 1f + val h1t1HoverProps = HoverAnimProps( + targets.get(1), + h1t1TransX, startBounds.top.toFloat(), h1t1ScaleX, 1f, + Rect( + h1t1TransX.toInt(), 0, (h1t1TransX + stageWidth).toInt(), + farStartBounds.bottom + ) + ) + + + // move target 2 to the very end + val h1t2TransX = (endBounds.left + farStartBounds.width()).toFloat() + val h1t2ScaleX = h1t1ScaleX + val h1t2HoverProps = HoverAnimProps( + targets.get(2), + h1t2TransX, endBounds.top.toFloat(), h1t2ScaleX, 1f, + Rect( + endBounds.left + farStartBounds.width(), + 0, + (endBounds.left + farStartBounds.width() + stageWidth), + farStartBounds.bottom + ) + ) + + + // move target 3 off-screen, default laid out is off-screen + val h1t3TransX = farEndBounds.right.toFloat() + val h1t3ScaleX = 1f + val h1t3HoverProps = HoverAnimProps( + targets.get(3), + h1t3TransX, farEndBounds.top.toFloat(), h1t3ScaleX, 1f, + null + ) + val animPropsForHoverTarget1 = + listOf(h1t0HoverProps, h1t1HoverProps, h1t2HoverProps, h1t3HoverProps) + + + // Hovering over target 2, + // * Ensure Target 0 offscreen + // * Ensure target 1 back to start, slightly smaller scale + // * Slightly lower scale of target 2 + // * Bring target 4 on screen + // reset target 0 + val h2t0TransX = farStartBounds.left.toFloat() + val h2t0ScaleX = 1f + val h2t0HoverProps = HoverAnimProps( + targets.get(0), + h2t0TransX, farStartBounds.top.toFloat(), h2t0ScaleX, 1f, + null + ) + + + // move target 1 over a tiny bit by same amount and make it smaller + val h2t1TransX = startBounds.left.toFloat() + val h2t1ScaleX = 1f + val h2t1HoverProps = HoverAnimProps( + targets.get(1), + h2t1TransX, startBounds.top.toFloat(), h2t1ScaleX, 1f, + Rect( + startBounds.left, 0, + (startBounds.left + stageWidth), + farStartBounds.bottom + ) + ) + + + // move target 2 to the very end + val h2t2TransX = endBounds.left.toFloat() + val h2t2ScaleX = h2t1ScaleX + val h2t2HoverProps = HoverAnimProps( + targets.get(2), + h2t2TransX, endBounds.top.toFloat(), h2t2ScaleX, 1f, + Rect( + (startBounds.right + dividerWidth).toInt(), + 0, + endBounds.left + stageWidth, + farStartBounds.bottom + ) + ) + + + // bring target 3 on-screen + val h2t3TransX = (farEndBounds.left - farEndBounds.width()).toFloat() + val h2t3ScaleX = 1f + val h2t3HoverProps = HoverAnimProps( + targets.get(3), + h2t3TransX, farEndBounds.top.toFloat(), h2t3ScaleX, 1f, + Rect( + endBounds.right, + 0, + displayRegion.right, + farStartBounds.bottom + ) + ) + val animPropsForHoverTarget2 = + listOf(h2t0HoverProps, h2t1HoverProps, h2t2HoverProps, h2t3HoverProps) + + + // Hovering over target 3, + // * Ensure Target 0 offscreen + // * Ensure target 1 back to start, slightly smaller scale + // * Slightly lower scale of target 2 + // * Bring target 4 on screen and scale up + // reset target 0 + val h3t0TransX = farStartBounds.left.toFloat() + val h3t0ScaleX = 1f + val h3t0HoverProps = HoverAnimProps( + targets.get(0), + h3t0TransX, farStartBounds.top.toFloat(), h3t0ScaleX, 1f, + null + ) + + + // move target 1 over a tiny bit by same amount and make it smaller + val h3t1ScaleX = endsWidth.toFloat() / stageWidth + val h3t1TransX = 0 - (stageWidth / (1 / h3t1ScaleX)) + val h3t1HoverProps = HoverAnimProps( + targets.get(1), + h3t1TransX, startBounds.top.toFloat(), h3t1ScaleX, 1f, + Rect( + 0, 0, + endsWidth, + farStartBounds.bottom + ) + ) + + + // move target 2 towards the start + val h3t2TransX: Float = endsWidth + dividerWidth + val h3t2ScaleX = 1f + val h3t2HoverProps = HoverAnimProps( + targets.get(2), + h3t2TransX, endBounds.top.toFloat(), h3t2ScaleX, 1f, + Rect( + endsWidth, 0, + (endsWidth + stageWidth + dividerWidth).toInt(), + farStartBounds.bottom + ) + ) + + + // bring target 3 on-screen and expand + val h3t3ScaleX = stageWidth.toFloat() / endsWidth + val h3t3TransX = endBounds.right - stageWidth / 2f + val h3t3HoverProps = HoverAnimProps( + targets.get(3), + h3t3TransX, farEndBounds.top.toFloat(), h3t3ScaleX, 1f, + Rect( + displayRegion.right - stageWidth, 0, + displayRegion.right, + farStartBounds.bottom + ) + ) + val animPropsForHoverTarget3 = + listOf(h3t0HoverProps, h3t1HoverProps, h3t2HoverProps, h3t3HoverProps) + + return Pair(targets, listOf(animPropsForHoverTarget0, animPropsForHoverTarget1, + animPropsForHoverTarget2, animPropsForHoverTarget3)) + + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index 268c3a20a41a..537ef182bb04 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -350,7 +350,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } cancelPhysicsAnimation(); mMenuController.hideMenu(ANIM_TYPE_DISMISS, false /* resize */); - // mPipTaskOrganizer.removePip(); + mPipScheduler.removePipAfterAnimation(); } /** Sets the movement bounds to use to constrain PIP position animations. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index d4f190ebd2a2..fbbf6f3596d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -19,81 +19,40 @@ package com.android.wm.shell.pip2.phone; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.graphics.Matrix; import android.graphics.Rect; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; -import androidx.annotation.IntDef; import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; -import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; +import com.android.wm.shell.pip2.animation.PipAlphaAnimator; import com.android.wm.shell.protolog.ShellProtoLogGroup; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * Scheduler for Shell initiated PiP transitions and animations. */ public class PipScheduler { private static final String TAG = PipScheduler.class.getSimpleName(); - private static final String BROADCAST_FILTER = PipScheduler.class.getCanonicalName(); private final Context mContext; private final PipBoundsState mPipBoundsState; private final ShellExecutor mMainExecutor; private final PipTransitionState mPipTransitionState; - private PipSchedulerReceiver mSchedulerReceiver; private PipTransitionController mPipTransitionController; + private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory + mSurfaceControlTransactionFactory; @Nullable private Runnable mUpdateMovementBoundsRunnable; - /** - * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell. - * This is used for a broadcast receiver to resolve intents. This should be removed once - * there is an equivalent of PipTouchHandler and PipResizeGestureHandler for PiP2. - */ - private static final int PIP_EXIT_VIA_EXPAND_CODE = 0; - private static final int PIP_DOUBLE_TAP = 1; - - @IntDef(value = { - PIP_EXIT_VIA_EXPAND_CODE, - PIP_DOUBLE_TAP - }) - @Retention(RetentionPolicy.SOURCE) - @interface PipUserJourneyCode {} - - /** - * A temporary broadcast receiver to initiate PiP CUJs. - */ - private class PipSchedulerReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - int userJourneyCode = intent.getIntExtra("cuj_code_extra", 0); - switch (userJourneyCode) { - case PIP_EXIT_VIA_EXPAND_CODE: - scheduleExitPipViaExpand(); - break; - case PIP_DOUBLE_TAP: - scheduleDoubleTapToResize(); - break; - default: - throw new IllegalStateException("unexpected CUJ code=" + userJourneyCode); - } - } - } - public PipScheduler(Context context, PipBoundsState pipBoundsState, ShellExecutor mainExecutor, @@ -103,12 +62,8 @@ public class PipScheduler { mMainExecutor = mainExecutor; mPipTransitionState = pipTransitionState; - if (PipUtils.isPip2ExperimentEnabled()) { - // temporary broadcast receiver to initiate exit PiP via expand - mSchedulerReceiver = new PipSchedulerReceiver(); - ContextCompat.registerReceiver(mContext, mSchedulerReceiver, - new IntentFilter(BROADCAST_FILTER), ContextCompat.RECEIVER_EXPORTED); - } + mSurfaceControlTransactionFactory = + new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); } ShellExecutor getMainExecutor() { @@ -133,6 +88,18 @@ public class PipScheduler { return wct; } + @Nullable + private WindowContainerTransaction getRemovePipTransaction() { + if (mPipTransitionState.mPipTaskToken == null) { + return null; + } + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mPipTransitionState.mPipTaskToken, null); + wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED); + wct.reorder(mPipTransitionState.mPipTaskToken, false); + return wct; + } + /** * Schedules exit PiP via expand transition. */ @@ -146,10 +113,26 @@ public class PipScheduler { } } - /** - * Schedules resize PiP via double tap. - */ - public void scheduleDoubleTapToResize() {} + // TODO: Optimize this by running the animation as part of the transition + /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */ + public void removePipAfterAnimation() { + SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); + PipAlphaAnimator animator = new PipAlphaAnimator(mContext, + mPipTransitionState.mPinnedTaskLeash, tx, PipAlphaAnimator.FADE_OUT); + animator.setAnimationEndCallback(this::scheduleRemovePipImmediately); + animator.start(); + } + + /** Schedules remove PiP transition. */ + private void scheduleRemovePipImmediately() { + WindowContainerTransaction wct = getRemovePipTransaction(); + if (wct != null) { + mMainExecutor.execute(() -> { + mPipTransitionController.startExitTransition(TRANSIT_REMOVE_PIP, wct, + null /* destinationBounds */); + }); + } + } /** * Animates resizing of the pinned stack given the duration. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java index c58de2c3512a..373ec806c40c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java @@ -90,9 +90,10 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener, if (mPictureInPictureParams.equals(params)) { return; } - if (PipUtils.remoteActionsChanged(params.getActions(), mPictureInPictureParams.getActions()) + if (params != null && (PipUtils.remoteActionsChanged(params.getActions(), + mPictureInPictureParams.getActions()) || !PipUtils.remoteActionsMatch(params.getCloseAction(), - mPictureInPictureParams.getCloseAction())) { + mPictureInPictureParams.getCloseAction()))) { for (PipParamsChangedCallback listener : mPipParamsChangedListeners) { listener.onActionsChanged(params.getActions(), params.getCloseAction()); } 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 029f001401c5..a4a7973ef4bb 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 @@ -582,7 +582,6 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha return true; } - /* if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting()) && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) { // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event @@ -599,6 +598,7 @@ public class PipTouchHandler implements PipTransitionState.PipTransitionStateCha return true; } + /* // Ignore the motion event When the entry animation is waiting to be started if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) { ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE, 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 b57f51aff176..ac1567aba6e9 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 @@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; 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 android.animation.Animator; @@ -605,8 +606,11 @@ public class PipTransition extends PipTransitionController implements && pipChange.getMode() == TRANSIT_TO_BACK; boolean isPipClosed = info.getType() == TRANSIT_CLOSE && pipChange.getMode() == TRANSIT_CLOSE; - // PiP is being removed if the pinned task is either moved to back or closed. - return isPipMovedToBack || isPipClosed; + // If PiP is dismissed by user (i.e. via dismiss button in PiP menu) + boolean isPipDismissed = info.getType() == TRANSIT_REMOVE_PIP + && pipChange.getMode() == TRANSIT_TO_BACK; + // PiP is being removed if the pinned task is either moved to back, closed, or dismissed. + return isPipMovedToBack || isPipClosed || isPipDismissed; } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 59aa7926ce8f..cf2c3dafed80 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -55,11 +55,6 @@ interface ISplitScreen { oneway void unregisterSplitSelectListener(in ISplitSelectListener listener) = 21; /** - * Removes a task from the side stage. - */ - oneway void removeFromSideStage(int taskId) = 4; - - /** * Removes the split-screen stages and leaving indicated task to top. Passing INVALID_TASK_ID * to indicate leaving no top task after leaving split-screen. */ 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 87b661d340ed..9e39f440915c 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 @@ -322,6 +322,22 @@ public class SplitScreenController implements SplitDragPolicy.Starter, return mTaskOrganizer.getRunningTaskInfo(taskId); } + /** + * @return an Array of RunningTaskInfo's ordered by leftToRight or topTopBottom + */ + @Nullable + public ActivityManager.RunningTaskInfo[] getAllTaskInfos() { + // TODO(b/349828130) Add the third stage task info and not rely on positions + ActivityManager.RunningTaskInfo topLeftTask = getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); + ActivityManager.RunningTaskInfo bottomRightTask = + getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); + if (topLeftTask != null && bottomRightTask != null) { + return new ActivityManager.RunningTaskInfo[]{topLeftTask, bottomRightTask}; + } + + return null; + } + /** Check task is under split or not by taskId. */ public boolean isTaskInSplitScreen(int taskId) { return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED; @@ -378,10 +394,6 @@ public class SplitScreenController implements SplitDragPolicy.Starter, return mStageCoordinator.moveToStage(task, stagePosition, wct); } - public boolean removeFromSideStage(int taskId) { - return mStageCoordinator.removeFromSideStage(taskId); - } - public void setSideStagePosition(@SplitPosition int sideStagePosition) { mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); } @@ -1177,12 +1189,6 @@ public class SplitScreenController implements SplitDragPolicy.Starter, } @Override - public void removeFromSideStage(int taskId) { - executeRemoteCallWithTaskPermission(mController, "removeFromSideStage", - (controller) -> controller.removeFromSideStage(taskId)); - } - - @Override public void startTask(int taskId, int position, @Nullable Bundle options) { executeRemoteCallWithTaskPermission(mController, "startTask", (controller) -> controller.startTask(taskId, position, options, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java index e1b474d9804a..a016a84616c3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenShellCommandHandler.java @@ -40,8 +40,6 @@ public class SplitScreenShellCommandHandler implements switch (args[0]) { case "moveToSideStage": return runMoveToSideStage(args, pw); - case "removeFromSideStage": - return runRemoveFromSideStage(args, pw); case "setSideStagePosition": return runSetSideStagePosition(args, pw); case "switchSplitPosition": @@ -67,17 +65,6 @@ public class SplitScreenShellCommandHandler implements return true; } - private boolean runRemoveFromSideStage(String[] args, PrintWriter pw) { - if (args.length < 2) { - // First argument is the action name. - pw.println("Error: task id should be provided as arguments"); - return false; - } - final int taskId = new Integer(args[1]); - mController.removeFromSideStage(taskId); - return true; - } - private boolean runSetSideStagePosition(String[] args, PrintWriter pw) { if (args.length < 2) { // First argument is the action name. @@ -109,8 +96,6 @@ public class SplitScreenShellCommandHandler implements public void printShellCommandHelp(PrintWriter pw, String prefix) { pw.println(prefix + "moveToSideStage <taskId> <SideStagePosition>"); pw.println(prefix + " Move a task with given id in split-screen mode."); - pw.println(prefix + "removeFromSideStage <taskId>"); - pw.println(prefix + " Remove a task with given id in split-screen mode."); pw.println(prefix + "setSideStagePosition <SideStagePosition>"); pw.println(prefix + " Sets the position of the side-stage."); pw.println(prefix + "switchSplitPosition"); 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 e527c02e0dec..3e76403de51b 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 @@ -35,12 +35,19 @@ import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; +import static com.android.wm.shell.common.split.SplitScreenUtils.isPartiallyOffscreen; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.TransitionUtil.isClosingType; import static com.android.wm.shell.shared.TransitionUtil.isOpeningType; +import static com.android.wm.shell.shared.TransitionUtil.isOrderOnly; import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -117,6 +124,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.policy.FoldLockSettingsObserver; import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -160,19 +168,19 @@ import java.util.concurrent.Executor; * - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are * visible * - Both stages are put under a single-top root task. - * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and - * {@link #onStageHasChildrenChanged(StageListenerImpl).} */ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler, - ShellTaskOrganizer.TaskListener { + ShellTaskOrganizer.TaskListener, StageTaskListener.StageListenerCallbacks { private static final String TAG = StageCoordinator.class.getSimpleName(); + // The duration in ms to prevent launch-adjacent from working after split screen is first + // entered + private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000; + private final StageTaskListener mMainStage; - private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final StageTaskListener mSideStage; - private final StageListenerImpl mSideStageListener = new StageListenerImpl(); @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -235,6 +243,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private SplitScreen.SplitInvocationListener mSplitInvocationListener; private Executor mSplitInvocationListenerExecutor; + // Re-enables launch-adjacent handling on the split root task. This needs to be a member + // because we will be posting and removing it from the handler. + private final Runnable mReEnableLaunchAdjacentOnRoot = () -> setLaunchAdjacentDisabled(false); + /** * Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support * CompatUI layouts. CompatUI is handled separately by MainStage and SideStage. @@ -328,7 +340,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mContext, mTaskOrganizer, mDisplayId, - mMainStageListener, + this /*stageListenerCallbacks*/, mSyncQueue, iconProvider, mWindowDecorViewModel); @@ -336,7 +348,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mContext, mTaskOrganizer, mDisplayId, - mSideStageListener, + this /*stageListenerCallbacks*/, mSyncQueue, iconProvider, mWindowDecorViewModel); @@ -411,7 +423,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } public boolean isSplitScreenVisible() { - return mSideStageListener.mVisible && mMainStageListener.mVisible; + return mSideStage.mVisible && mMainStage.mVisible; } private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) { @@ -499,21 +511,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - boolean removeFromSideStage(int taskId) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - - // MainStage will be deactivated in onStageHasChildrenChanged() if the other stages - // no longer have children. - - final boolean result = mSideStage.removeTask(taskId, - isSplitActive() ? mMainStage.mRootTaskInfo.token : null, - wct); - mTaskOrganizer.applyTransaction(wct); - return result; - } - SplitscreenEventLogger getLogger() { return mLogger; } @@ -1111,7 +1108,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStagePosition = sideStagePosition; sendOnStagePositionChanged(); - if (mSideStageListener.mVisible && updateBounds) { + if (mSideStage.mVisible && updateBounds) { if (wct == null) { // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds. onLayoutSizeChanged(mSplitLayout); @@ -1332,8 +1329,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void clearRequestIfPresented() { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented"); - if (mSideStageListener.mVisible && mSideStageListener.mHasChildren - && mMainStageListener.mVisible && mSideStageListener.mHasChildren) { + if (mSideStage.mVisible && mSideStage.mHasChildren + && mMainStage.mVisible && mSideStage.mHasChildren) { mSplitRequest = null; } } @@ -1586,11 +1583,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId, + @Override + public void onChildTaskStatusChanged(StageTaskListener stageListener, int taskId, boolean present, boolean visible) { int stage; if (present) { - stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; + stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; } else { // No longer on any stage stage = STAGE_TYPE_UNDEFINED; @@ -1721,13 +1719,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting - void onRootTaskAppeared() { + @Override + public void onRootTaskAppeared() { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b", - mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask); + mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask); // Wait unit all root tasks appeared. if (mRootTaskInfo == null - || !mMainStageListener.mHasRootTask - || !mSideStageListener.mHasRootTask) { + || !mMainStage.mHasRootTask + || !mSideStage.mHasRootTask) { return; } @@ -1747,35 +1746,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token); } - /** Callback when split roots have child task appeared under it, this is a little different from - * #onStageHasChildrenChanged because this would be called every time child task appeared. - * NOTICE: This only be called on legacy transition. */ - private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d", - stageListener == mMainStageListener, taskId); - // Handle entering split screen while there is a split pair running in the background. - if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive() - && mSplitRequest == null) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareEnterSplitScreen(wct); - mMainStage.evictAllChildren(wct); - mSideStage.evictOtherChildren(wct, taskId); - - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> { - if (mIsDropEntering) { - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - mIsDropEntering = false; - mSkipEvictingMainStageChildren = false; - } else { - mShowDecorImmediately = true; - mSplitLayout.flingDividerToCenter(/*finishCallback*/ null); - } - }); - } - } - - private void onRootTaskVanished() { + @Override + public void onRootTaskVanished() { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished"); final WindowContainerTransaction wct = new WindowContainerTransaction(); mLaunchAdjacentController.clearLaunchAdjacentRoot(); @@ -1792,15 +1764,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Callback when split roots visiblility changed. * NOTICE: This only be called on legacy transition. */ - private void onStageVisibilityChanged(StageListenerImpl stageListener) { + @Override + public void onStageVisibilityChanged(StageTaskListener stageListener) { // If split didn't active, just ignore this callback because we should already did these // on #applyExitSplitScreen. if (!isSplitActive()) { return; } - final boolean sideStageVisible = mSideStageListener.mVisible; - final boolean mainStageVisible = mMainStageListener.mVisible; + final boolean sideStageVisible = mSideStage.mVisible; + final boolean mainStageVisible = mMainStage.mVisible; // Wait for both stages having the same visibility to prevent causing flicker. if (mainStageVisible != sideStageVisible) { @@ -1937,18 +1910,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Callback when split roots have child or haven't under it. * NOTICE: This only be called on legacy transition. */ - private void onStageHasChildrenChanged(StageListenerImpl stageListener) { + @Override + public void onStageHasChildrenChanged(StageTaskListener stageListener) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b", - stageListener == mMainStageListener); + stageListener == mMainStage); final boolean hasChildren = stageListener.mHasChildren; - final boolean isSideStage = stageListener == mSideStageListener; + final boolean isSideStage = stageListener == mSideStage; if (!hasChildren && !mIsExiting && isSplitActive()) { - if (isSideStage && mMainStageListener.mVisible) { + if (isSideStage && mMainStage.mVisible) { // Exit to main stage if side stage no longer has children. mSplitLayout.flingDividerToDismiss( mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT, EXIT_REASON_APP_FINISHED); - } else if (!isSideStage && mSideStageListener.mVisible) { + } else if (!isSideStage && mSideStage.mVisible) { // Exit to side stage if main stage no longer has children. mSplitLayout.flingDividerToDismiss( mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT, @@ -1973,7 +1947,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }); } - if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { + if (mMainStage.mHasChildren && mSideStage.mHasChildren) { mShouldUpdateRecents = true; clearRequestIfPresented(); updateRecentTasksSplitPair(); @@ -1989,6 +1963,35 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override + public void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener, + ActivityManager.RunningTaskInfo taskInfo) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo); + if (isSplitActive()) { + final boolean isMainStage = mMainStage == stageTaskListener; + + // If visible, we preserve the app and keep it running. If an app becomes + // unsupported in the bg, break split without putting anything on top + boolean splitScreenVisible = isSplitScreenVisible(); + int stageType = STAGE_TYPE_UNDEFINED; + if (splitScreenVisible) { + stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + } + final WindowContainerTransaction wct = new WindowContainerTransaction(); + prepareExitSplitScreen(stageType, wct); + clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); + mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType, + EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); + Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow", + "app package " + taskInfo.baseIntent.getComponent() + + " does not support splitscreen, or is a controlled activity" + + " type")); + if (splitScreenVisible) { + handleUnsupportedSplitStart(); + } + } + } + + @Override public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s", bottomOrRight, exitReasonToString(exitReason)); @@ -2060,6 +2063,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish"); }, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager()); + if (Flags.enableFlexibleTwoAppSplit()) { + switch (layout.calculateCurrentSnapPosition()) { + case SNAP_TO_2_10_90 -> grantFocusToPosition(false /* leftOrTop */); + case SNAP_TO_2_90_10 -> grantFocusToPosition(true /* leftOrTop */); + } + } + mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); } @@ -2513,6 +2523,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } continue; + } else if (Flags.enableFlexibleTwoAppSplit() && isOrderOnly(change)) { + int focusedStageIndex = SPLIT_INDEX_UNDEFINED; + if (taskInfo.token.equals(mMainStage.mRootTaskInfo.token)) { + focusedStageIndex = mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT + ? SPLIT_INDEX_0 : SPLIT_INDEX_1; + } else if (taskInfo.token.equals(mSideStage.mRootTaskInfo.token)) { + focusedStageIndex = mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT + ? SPLIT_INDEX_1 : SPLIT_INDEX_0; + } + + if (focusedStageIndex != SPLIT_INDEX_UNDEFINED) { + @PersistentSnapPosition int currentSnapPosition = + mSplitLayout.calculateCurrentSnapPosition(); + boolean offscreenTaskFocused = + isPartiallyOffscreen(focusedStageIndex, currentSnapPosition); + + if (offscreenTaskFocused) { + mSplitLayout.flingDividerToOtherSide(currentSnapPosition); + } + } } final StageTaskListener stage = getStageOfTask(taskInfo); if (stage == null) { @@ -2677,6 +2707,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + /** + * Sets whether launch-adjacent is disabled or enabled. + */ + private void setLaunchAdjacentDisabled(boolean disabled) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLaunchAdjacentDisabled: disabled=%b", disabled); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setDisableLaunchAdjacent(mRootTaskInfo.token, disabled); + mTaskOrganizer.applyTransaction(wct); + } + /** Starts the pending transition animation. */ public boolean startPendingAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @@ -2689,6 +2729,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitTransitions.isPendingEnter(transition)) { shouldAnimate = startPendingEnterAnimation(transition, mSplitTransitions.mPendingEnter, info, startTransaction, finishTransaction); + + // Disable launch adjacent after an enter animation to prevent cases where apps are + // incorrectly trampolining and incorrectly triggering a double launch-adjacent task + // launch (ie. main -> split -> main). See b/344216031 + setLaunchAdjacentDisabled(true); + mMainHandler.removeCallbacks(mReEnableLaunchAdjacentOnRoot); + mMainHandler.postDelayed(mReEnableLaunchAdjacentOnRoot, + DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS); } else if (mSplitTransitions.isPendingDismiss(transition)) { final SplitScreenTransitions.DismissSession dismiss = mSplitTransitions.mPendingDismiss; shouldAnimate = startPendingDismissAnimation( @@ -3197,13 +3245,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition())); pw.println(childPrefix + "isActive=" + isSplitActive()); mMainStage.dump(pw, childPrefix); - pw.println(innerPrefix + "MainStageListener"); - mMainStageListener.dump(pw, childPrefix); pw.println(innerPrefix + "SideStage"); pw.println(childPrefix + "stagePosition=" + splitPositionToString(getSideStagePosition())); mSideStage.dump(pw, childPrefix); - pw.println(innerPrefix + "SideStageListener"); - mSideStageListener.dump(pw, childPrefix); if (mSplitLayout != null) { mSplitLayout.dump(pw, childPrefix); } @@ -3219,8 +3263,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ private void setSplitsVisible(boolean visible) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible); - mMainStageListener.mVisible = mSideStageListener.mVisible = visible; - mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible; + mMainStage.mVisible = mSideStage.mVisible = visible; + mMainStage.mHasChildren = mSideStage.mHasChildren = visible; } /** @@ -3270,86 +3314,4 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */, mSplitLayout.isLeftRightSplit()); } - - class StageListenerImpl implements StageTaskListener.StageListenerCallbacks { - boolean mHasRootTask = false; - boolean mVisible = false; - boolean mHasChildren = false; - - @Override - public void onRootTaskAppeared() { - mHasRootTask = true; - StageCoordinator.this.onRootTaskAppeared(); - } - - @Override - public void onChildTaskAppeared(int taskId) { - StageCoordinator.this.onChildTaskAppeared(this, taskId); - } - - @Override - public void onStatusChanged(boolean visible, boolean hasChildren) { - if (!mHasRootTask) return; - - if (mHasChildren != hasChildren) { - mHasChildren = hasChildren; - StageCoordinator.this.onStageHasChildrenChanged(this); - } - if (mVisible != visible) { - mVisible = visible; - StageCoordinator.this.onStageVisibilityChanged(this); - } - } - - @Override - public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) { - StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible); - } - - @Override - public void onRootTaskVanished() { - reset(); - StageCoordinator.this.onRootTaskVanished(); - } - - @Override - public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo); - if (isSplitActive()) { - final boolean isMainStage = mMainStageListener == this; - - // If visible, we preserve the app and keep it running. If an app becomes - // unsupported in the bg, break split without putting anything on top - boolean splitScreenVisible = isSplitScreenVisible(); - int stageType = STAGE_TYPE_UNDEFINED; - if (splitScreenVisible) { - stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; - } - final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareExitSplitScreen(stageType, wct); - clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); - mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType, - EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); - Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow", - "app package " + taskInfo.baseIntent.getComponent() - + " does not support splitscreen, or is a controlled activity" - + " type")); - if (splitScreenVisible) { - handleUnsupportedSplitStart(); - } - } - } - - private void reset() { - mHasRootTask = false; - mVisible = false; - mHasChildren = false; - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - pw.println(prefix + "mHasRootTask=" + mHasRootTask); - pw.println(prefix + "mVisible=" + mVisible); - pw.println(prefix + "mHasChildren=" + mHasChildren); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index d64c0a24be68..ae4bd1615ae1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -22,20 +22,17 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; -import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.annotation.CallSuper; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; -import android.util.Slog; import android.util.SparseArray; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -74,20 +71,21 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the // stages should have this be set/being used private boolean mIsActive; - /** Callback interface for listening to changes in a split-screen stage. */ public interface StageListenerCallbacks { void onRootTaskAppeared(); - void onChildTaskAppeared(int taskId); + void onStageHasChildrenChanged(StageTaskListener stageTaskListener); - void onStatusChanged(boolean visible, boolean hasChildren); + void onStageVisibilityChanged(StageTaskListener stageTaskListener); - void onChildTaskStatusChanged(int taskId, boolean present, boolean visible); + void onChildTaskStatusChanged(StageTaskListener stage, int taskId, boolean present, + boolean visible); void onRootTaskVanished(); - void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo); + void onNoLongerSupportMultiWindow(StageTaskListener stageTaskListener, + ActivityManager.RunningTaskInfo taskInfo); } private final Context mContext; @@ -96,6 +94,12 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { private final IconProvider mIconProvider; private final Optional<WindowDecorViewModel> mWindowDecorViewModel; + /** Whether or not the root task has been created. */ + boolean mHasRootTask = false; + /** Whether or not the root task is visible. */ + boolean mVisible = false; + /** Whether or not the root task has any children or not. */ + boolean mHasChildren = false; protected ActivityManager.RunningTaskInfo mRootTaskInfo; protected SurfaceControl mRootLeash; protected SurfaceControl mDimLayer; @@ -201,6 +205,7 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { mSplitDecorManager = new SplitDecorManager( mRootTaskInfo.configuration, mIconProvider); + mHasRootTask = true; mCallbacks.onRootTaskAppeared(); sendStatusChanged(); mSyncQueue.runInSync(t -> mDimLayer = @@ -209,15 +214,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); mChildrenTaskInfo.put(taskId, taskInfo); - mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, + mCallbacks.onChildTaskStatusChanged(this, taskId, true /* present */, taskInfo.isVisible && taskInfo.isVisibleRequested); - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); - mCallbacks.onChildTaskAppeared(taskId); - sendStatusChanged(); } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + "\n mRootTaskInfo: " + mRootTaskInfo); @@ -231,14 +229,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { taskInfo.taskId, taskInfo.baseActivity); mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo)); if (mRootTaskInfo.taskId == taskInfo.taskId) { - // Inflates split decor view only when the root task is visible. - if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) { - if (taskInfo.isVisible) { - mSplitDecorManager.inflate(mContext, mRootLeash); - } else { - mSyncQueue.runInSync(t -> mSplitDecorManager.release(t)); - } - } mRootTaskInfo = taskInfo; } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { if (!taskInfo.supportsMultiWindow @@ -250,25 +240,16 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { taskInfo.taskId); // Leave split screen if the task no longer supports multi window or have // uncontrolled task. - mCallbacks.onNoLongerSupportMultiWindow(taskInfo); + mCallbacks.onNoLongerSupportMultiWindow(this, taskInfo); return; } mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, + mCallbacks.onChildTaskStatusChanged(this, taskInfo.taskId, true /* present */, taskInfo.isVisible && taskInfo.isVisibleRequested); - if (!ENABLE_SHELL_TRANSITIONS) { - updateChildTaskSurface( - taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); - } } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + "\n mRootTaskInfo: " + mRootTaskInfo); } - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); } @Override @@ -278,6 +259,9 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { final int taskId = taskInfo.taskId; mWindowDecorViewModel.ifPresent(vm -> vm.onTaskVanished(taskInfo)); if (mRootTaskInfo.taskId == taskId) { + mHasRootTask = false; + mVisible = false; + mHasChildren = false; mCallbacks.onRootTaskVanished(); mRootTaskInfo = null; mRootLeash = null; @@ -288,12 +272,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } else if (mChildrenTaskInfo.contains(taskId)) { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); - mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); + mCallbacks.onChildTaskStatusChanged(this, taskId, false /* present */, + taskInfo.isVisible); } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo + "\n mRootTaskInfo: " + mRootTaskInfo); @@ -455,26 +435,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } - private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo, - SurfaceControl leash, boolean firstAppeared) { - final Point taskPositionInParent = taskInfo.positionInParent; - mSyncQueue.runInSync(t -> { - // The task surface might be released before running in the sync queue for the case like - // trampoline launch, so check if the surface is valid before processing it. - if (!leash.isValid()) { - Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId); - return; - } - t.setCrop(leash, null); - t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); - if (firstAppeared) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } - }); - } - // --------- // Previously only used in MainStage boolean isActive() { @@ -538,7 +498,19 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } private void sendStatusChanged() { - mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0); + boolean hasChildren = mChildrenTaskInfo.size() > 0; + boolean visible = mRootTaskInfo.isVisible; + if (!mHasRootTask) return; + + if (mHasChildren != hasChildren) { + mHasChildren = hasChildren; + mCallbacks.onStageHasChildrenChanged(this); + } + + if (mVisible != visible) { + mVisible = visible; + mCallbacks.onStageVisibilityChanged(this); + } } @Override @@ -554,5 +526,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { + " baseActivity=" + taskInfo.baseActivity); } } + pw.println(prefix + "mHasRootTask=" + mHasRootTask); + pw.println(prefix + "mVisible=" + mVisible); + pw.println(prefix + "mHasChildren=" + mHasChildren); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index a2439a937512..5437167f58d5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -61,6 +61,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN; +import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation; import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow; import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet; import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo; @@ -68,8 +69,6 @@ import static com.android.wm.shell.transition.TransitionAnimationHelper.isCovere import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; @@ -81,7 +80,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Color; -import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -90,12 +88,10 @@ import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; import android.util.ArrayMap; -import android.view.Choreographer; import android.view.SurfaceControl; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; -import android.view.animation.Transformation; import android.window.TransitionInfo; import android.window.TransitionMetrics; import android.window.TransitionRequestInfo; @@ -362,7 +358,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS; if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) { final int flags = wallpaperTransit != WALLPAPER_TRANSITION_NONE - && Flags.commonSurfaceAnimator() ? ScreenRotationAnimation.FLAG_HAS_WALLPAPER : 0; startRotationAnimation(startTransaction, change, info, anim, flags, animations, onAnimFinish); @@ -823,72 +818,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return a; } - /** Builds an animator for the surface and adds it to the `animations` list. */ - static void buildSurfaceAnimation(@NonNull ArrayList<Animator> animations, - @NonNull Animation anim, @NonNull SurfaceControl leash, - @NonNull Runnable finishCallback, @NonNull TransactionPool pool, - @NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius, - @Nullable Rect clipRect, boolean isActivity) { - if (Flags.commonSurfaceAnimator()) { - DefaultSurfaceAnimator.buildSurfaceAnimation(animations, anim, leash, finishCallback, - pool, mainExecutor, position, cornerRadius, clipRect, isActivity); - return; - } - final SurfaceControl.Transaction transaction = pool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); - final Transformation transformation = new Transformation(); - final float[] matrix = new float[9]; - // Animation length is already expected to be scaled. - va.overrideDurationScale(1.0f); - va.setDuration(anim.computeDurationHint()); - final ValueAnimator.AnimatorUpdateListener updateListener = animation -> { - final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime()); - - applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix, - position, cornerRadius, clipRect, isActivity); - }; - va.addUpdateListener(updateListener); - - final Runnable finisher = () -> { - applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix, - position, cornerRadius, clipRect, isActivity); - - pool.release(transaction); - mainExecutor.execute(() -> { - animations.remove(va); - finishCallback.run(); - }); - }; - va.addListener(new AnimatorListenerAdapter() { - // It is possible for the end/cancel to be called more than once, which may cause - // issues if the animating surface has already been released. Track the finished - // state here to skip duplicate callbacks. See b/252872225. - private boolean mFinished = false; - - @Override - public void onAnimationEnd(Animator animation) { - onFinish(); - } - - @Override - public void onAnimationCancel(Animator animation) { - onFinish(); - } - - private void onFinish() { - if (mFinished) return; - mFinished = true; - finisher.run(); - // The update listener can continue to be called after the animation has ended if - // end() is called manually again before the finisher removes the animation. - // Remove it manually here to prevent animating a released surface. - // See b/252872225. - va.removeUpdateListener(updateListener); - } - }); - animations.add(va); - } - private void attachThumbnail(@NonNull ArrayList<Animator> animations, @NonNull Runnable finishCallback, TransitionInfo.Change change, TransitionInfo.AnimationOptions options, float cornerRadius) { @@ -1014,38 +943,4 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { || animType == ANIM_CLIP_REVEAL || animType == ANIM_OPEN_CROSS_PROFILE_APPS || animType == ANIM_FROM_STYLE; } - - private static void applyTransformation(long time, SurfaceControl.Transaction t, - SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix, - Point position, float cornerRadius, @Nullable Rect immutableClipRect, - boolean isActivity) { - tmpTransformation.clear(); - anim.getTransformation(time, tmpTransformation); - if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader() - && anim.getExtensionEdges() != 0x0 && isActivity) { - t.setEdgeExtensionEffect(leash, anim.getExtensionEdges()); - } - if (position != null) { - tmpTransformation.getMatrix().postTranslate(position.x, position.y); - } - t.setMatrix(leash, tmpTransformation.getMatrix(), matrix); - t.setAlpha(leash, tmpTransformation.getAlpha()); - - final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect); - Insets extensionInsets = Insets.min(tmpTransformation.getInsets(), Insets.NONE); - if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) { - // Clip out any overflowing edge extension - clipRect.inset(extensionInsets); - t.setCrop(leash, clipRect); - } - - if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) { - // We can only apply rounded corner if a crop is set - t.setCrop(leash, clipRect); - t.setCornerRadius(leash, cornerRadius); - } - - t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); - t.apply(); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index 1a04997fa384..6f3aa11a8f52 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -21,7 +21,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFA import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT; import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE; -import static com.android.wm.shell.transition.DefaultTransitionHandler.buildSurfaceAnimation; +import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation; import static com.android.wm.shell.transition.Transitions.TAG; import android.animation.Animator; 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 2c621b1f1a52..d3b7ca15856f 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 @@ -514,8 +514,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin )); } else { mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData( - mTaskInfo, TaskInfoKt.getRequestingImmersive(mTaskInfo), inFullImmersive, - hasGlobalFocus + mTaskInfo, + TaskInfoKt.getRequestingImmersive(mTaskInfo), + inFullImmersive, + hasGlobalFocus, + /* maximizeHoverEnabled= */ canOpenMaximizeMenu( + /* animatingTaskResizeOrReposition= */ false) )); } Trace.endSection(); @@ -1616,8 +1620,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) { if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return; - asAppHeader(mWindowDecorViewHolder) - .setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition); + final boolean inFullImmersive = + mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId); + asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData( + mTaskInfo, + TaskInfoKt.getRequestingImmersive(mTaskInfo), + inFullImmersive, + isFocused(), + /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition))); } /** @@ -1634,6 +1644,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin asAppHeader(mWindowDecorViewHolder).onMaximizeWindowHoverEnter(); } + private boolean canOpenMaximizeMenu(boolean animatingTaskResizeOrReposition) { + if (!Flags.enableFullyImmersiveInDesktop()) { + return !animatingTaskResizeOrReposition; + } + final boolean inImmersiveAndRequesting = + mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId) + && TaskInfoKt.getRequestingImmersive(mTaskInfo); + return !animatingTaskResizeOrReposition && !inImmersiveAndRequesting; + } + @Override public String toString() { return "{" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index cf03b3f74dc7..d94391820d05 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -70,7 +70,7 @@ class AppHeaderViewHolder( rootView: View, onCaptionTouchListener: View.OnTouchListener, onCaptionButtonClickListener: View.OnClickListener, - onLongClickListener: OnLongClickListener, + private val onLongClickListener: OnLongClickListener, onCaptionGenericMotionListener: View.OnGenericMotionListener, appName: CharSequence, appIconBitmap: Bitmap, @@ -81,7 +81,8 @@ class AppHeaderViewHolder( val taskInfo: RunningTaskInfo, val isRequestingImmersive: Boolean, val inFullImmersiveState: Boolean, - val hasGlobalFocus: Boolean + val hasGlobalFocus: Boolean, + val enableMaximizeLongClick: Boolean, ) : Data() private val decorThemeUtil = DecorThemeUtil(context) @@ -160,19 +161,30 @@ class AppHeaderViewHolder( } override fun bindData(data: HeaderData) { - bindData(data.taskInfo, data.isRequestingImmersive, data.inFullImmersiveState, - data.hasGlobalFocus) + bindData( + data.taskInfo, + data.isRequestingImmersive, + data.inFullImmersiveState, + data.hasGlobalFocus, + data.enableMaximizeLongClick + ) } private fun bindData( taskInfo: RunningTaskInfo, isRequestingImmersive: Boolean, inFullImmersiveState: Boolean, - hasGlobalFocus: Boolean + hasGlobalFocus: Boolean, + enableMaximizeLongClick: Boolean, ) { if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) { - bindDataWithThemedHeaders(taskInfo, isRequestingImmersive, inFullImmersiveState, - hasGlobalFocus) + bindDataWithThemedHeaders( + taskInfo, + isRequestingImmersive, + inFullImmersiveState, + hasGlobalFocus, + enableMaximizeLongClick, + ) } else { bindDataLegacy(taskInfo, hasGlobalFocus) } @@ -215,7 +227,8 @@ class AppHeaderViewHolder( taskInfo: RunningTaskInfo, requestingImmersive: Boolean, inFullImmersiveState: Boolean, - hasGlobalFocus: Boolean + hasGlobalFocus: Boolean, + enableMaximizeLongClick: Boolean, ) { val header = fillHeaderInfo(taskInfo, hasGlobalFocus) val headerStyle = getHeaderStyle(header) @@ -281,6 +294,16 @@ class AppHeaderViewHolder( drawableInsets = closeDrawableInsets ) } + if (!enableMaximizeLongClick) { + maximizeButtonView.cancelHoverAnimation() + } + maximizeButtonView.hoverDisabled = !enableMaximizeLongClick + maximizeWindowButton.onLongClickListener = if (enableMaximizeLongClick) { + onLongClickListener + } else { + // Disable long-click to open maximize menu when in immersive. + null + } } override fun onHandleMenuOpened() {} @@ -291,14 +314,6 @@ class AppHeaderViewHolder( } } - fun setAnimatingTaskResizeOrReposition(animatingTaskResizeOrReposition: Boolean) { - // If animating a task resize or reposition, cancel any running hover animations - if (animatingTaskResizeOrReposition) { - maximizeButtonView.cancelHoverAnimation() - } - maximizeButtonView.hoverDisabled = animatingTaskResizeOrReposition - } - fun onMaximizeWindowHoverExit() { maximizeButtonView.cancelHoverAnimation() } @@ -364,6 +379,11 @@ class AppHeaderViewHolder( private fun shouldShowExitFullImmersiveIcon( requestingImmersive: Boolean, inFullImmersiveState: Boolean + ): Boolean = isInFullImmersiveStateAndRequesting(requestingImmersive, inFullImmersiveState) + + private fun isInFullImmersiveStateAndRequesting( + requestingImmersive: Boolean, + inFullImmersiveState: Boolean ): Boolean = Flags.enableFullyImmersiveInDesktop() && requestingImmersive && inFullImmersiveState diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt index 77423af667f7..43ee1866d026 100644 --- a/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/appcompat/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.appcompat import android.platform.test.annotations.Postsubmit -import android.tools.Rotation import android.tools.flicker.assertions.FlickerTest import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder @@ -91,9 +90,7 @@ class RepositionFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseAppCompat @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_90) - ) + return LegacyFlickerTestFactory.nonRotationTests() } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java index 1da4ef6b5a8b..266e48482568 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java @@ -53,6 +53,7 @@ public class BackProgressAnimatorTest { return new BackMotionEvent( /* touchX = */ touchX, /* touchY = */ 0, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt index 2235c20d7f21..2cc52c5ab9ad 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt @@ -221,6 +221,7 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() { BackMotionEvent( /* touchX = */ touchX, /* touchY = */ 0f, + /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, /* swipeEdge = */ BackEvent.EDGE_LEFT, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt index dde9fda13ea9..0825b6b0d7be 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -18,7 +18,10 @@ package com.android.wm.shell.desktopmode import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule +import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations +import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker import com.android.dx.mockito.inline.extended.ExtendedMockito.verify +import com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.window.flags.Flags @@ -26,15 +29,16 @@ import com.android.wm.shell.EventLogTags import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.NO_SESSION_ID import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskSizeUpdate -import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_MINIMIZE_REASON import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UNSET_UNMINIMIZE_REASON -import kotlinx.coroutines.runBlocking +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.UnminimizeReason +import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.mockito.kotlin.eq @@ -49,40 +53,87 @@ class DesktopModeEventLoggerTest : ShellTestCase() { @JvmField @Rule(order = 0) val extendedMockitoRule = ExtendedMockitoRule.Builder(this) - .mockStatic(FrameworkStatsLog::class.java) - .mockStatic(EventLogTags::class.java).build()!! + .mockStatic(FrameworkStatsLog::class.java) + .mockStatic(EventLogTags::class.java).build()!! @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule() @Test - fun logSessionEnter_enterReason() = runBlocking { - desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER) + fun logSessionEnter_logsEnterReasonWithNewSessionId() { + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + + val sessionId = desktopModeEventLogger.currentSessionId.get() + assertThat(sessionId).isNotEqualTo(NO_SESSION_ID) + verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), + /* enter_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER), + /* exit_reason */ + eq(0), + /* sessionId */ + eq(sessionId) + ) + } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verify { + EventLogTags.writeWmShellEnterDesktopMode( + eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason), + eq(sessionId) + ) + } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logSessionEnter_ongoingSession_logsEnterReasonWithNewSessionId() { + val previousSessionId = startDesktopModeSession() + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + + val sessionId = desktopModeEventLogger.currentSessionId.get() + assertThat(sessionId).isNotEqualTo(NO_SESSION_ID) + assertThat(sessionId).isNotEqualTo(previousSessionId) verify { FrameworkStatsLog.write( eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), /* event */ eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), /* enter_reason */ - eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER), + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER), /* exit_reason */ eq(0), /* sessionId */ - eq(SESSION_ID) + eq(sessionId) ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellEnterDesktopMode( - eq(EnterReason.UNKNOWN_ENTER.reason), - eq(SESSION_ID)) + eq(EnterReason.KEYBOARD_SHORTCUT_ENTER.reason), + eq(sessionId) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logSessionExit_exitReason() = runBlocking { - desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT) + fun logSessionExit_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logSessionExit_logsExitReasonAndClearsSessionId() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logSessionExit(ExitReason.DRAG_TO_EXIT) verify { FrameworkStatsLog.write( @@ -92,24 +143,39 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* enter_reason */ eq(0), /* exit_reason */ - eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT), + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT), /* sessionId */ - eq(SESSION_ID) + eq(sessionId) ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellExitDesktopMode( - eq(ExitReason.UNKNOWN_EXIT.reason), - eq(SESSION_ID)) + eq(ExitReason.DRAG_TO_EXIT.reason), + eq(sessionId) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + assertThat(desktopModeEventLogger.currentSessionId.get()).isEqualTo(NO_SESSION_ID) + } + + @Test + fun logTaskAdded_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskAdded(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskAdded_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskAdded_logsTaskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskAdded(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), /* instance_id */ @@ -125,36 +191,52 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskRemoved_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskRemoved(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskRemoved_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskRemoved_taskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskRemoved(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), /* instance_id */ @@ -170,39 +252,57 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskInfoChanged_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_taskUpdate() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE) + fun logTaskInfoChanged_taskUpdate() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged(TASK_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -216,40 +316,51 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, - createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT)) + fun logTaskInfoChanged_logsTaskUpdateWithMinimizeReason() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged( + createTaskUpdate(minimizeReason = MinimizeReason.TASK_LIMIT) + ) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -263,42 +374,53 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), /* minimize_reason */ eq(MinimizeReason.TASK_LIMIT.reason), /* unminimize_reason */ eq(UNSET_UNMINIMIZE_REASON), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(MinimizeReason.TASK_LIMIT.reason), eq(UNSET_UNMINIMIZE_REASON), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test - fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() = runBlocking { - desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, - createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP)) + fun logTaskInfoChanged_logsTaskUpdateWithUnminimizeReason() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskInfoChanged( + createTaskUpdate(unminimizeReason = UnminimizeReason.TASKBAR_TAP) + ) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), /* task_event */ - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), /* instance_id */ eq(TASK_UPDATE.instanceId), /* uid */ @@ -312,39 +434,55 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* task_y */ eq(TASK_UPDATE.taskY), /* session_id */ - eq(SESSION_ID), + eq(sessionId), /* minimize_reason */ eq(UNSET_MINIMIZE_REASON), /* unminimize_reason */ eq(UnminimizeReason.TASKBAR_TAP.reason), /* visible_task_count */ - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } - + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) verify { EventLogTags.writeWmShellDesktopModeTaskUpdate( - eq(FrameworkStatsLog - .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + eq( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED + ), eq(TASK_UPDATE.instanceId), eq(TASK_UPDATE.uid), eq(TASK_UPDATE.taskHeight), eq(TASK_UPDATE.taskWidth), eq(TASK_UPDATE.taskX), eq(TASK_UPDATE.taskY), - eq(SESSION_ID), + eq(sessionId), eq(UNSET_MINIMIZE_REASON), eq(UnminimizeReason.TASKBAR_TAP.reason), - eq(TASK_COUNT)) + eq(TASK_COUNT) + ) } + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) + } + + @Test + fun logTaskResizingStarted_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS) - fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() = runBlocking { - desktopModeEventLogger.logTaskResizingStarted(sessionId = SESSION_ID, createTaskSizeUpdate()) + fun logTaskResizingStarted_logsTaskSizeUpdatedWithStartResizingStage() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskResizingStarted(TASK_SIZE_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), /* resizing_stage */ @@ -352,7 +490,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* input_method */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD), /* desktop_mode_session_id */ - eq(SESSION_ID), + eq(sessionId), /* instance_id */ eq(TASK_SIZE_UPDATE.instanceId), /* uid */ @@ -365,15 +503,27 @@ class DesktopModeEventLoggerTest : ShellTestCase() { eq(TASK_SIZE_UPDATE.displayArea), ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + } + + @Test + fun logTaskResizingEnded_noOngoingSession_doesNotLog() { + desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) + + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + verifyZeroInteractions(staticMockMarker(EventLogTags::class.java)) } @Test @EnableFlags(Flags.FLAG_ENABLE_RESIZING_METRICS) - fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() = runBlocking { - desktopModeEventLogger.logTaskResizingEnded(sessionId = SESSION_ID, createTaskSizeUpdate()) + fun logTaskResizingEnded_logsTaskSizeUpdatedWithEndResizingStage() { + val sessionId = startDesktopModeSession() + + desktopModeEventLogger.logTaskResizingEnded(TASK_SIZE_UPDATE) verify { - FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED), /* resize_trigger */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__UNKNOWN_RESIZE_TRIGGER), /* resizing_stage */ @@ -381,7 +531,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { /* input_method */ eq(FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD), /* desktop_mode_session_id */ - eq(SESSION_ID), + eq(sessionId), /* instance_id */ eq(TASK_SIZE_UPDATE.instanceId), /* uid */ @@ -394,6 +544,14 @@ class DesktopModeEventLoggerTest : ShellTestCase() { eq(TASK_SIZE_UPDATE.displayArea), ) } + verifyZeroInteractions(staticMockMarker(FrameworkStatsLog::class.java)) + } + + private fun startDesktopModeSession(): Int { + desktopModeEventLogger.logSessionEnter(EnterReason.KEYBOARD_SHORTCUT_ENTER) + clearInvocations(staticMockMarker(FrameworkStatsLog::class.java)) + clearInvocations(staticMockMarker(EventLogTags::class.java)) + return desktopModeEventLogger.currentSessionId.get() } @Test @@ -428,7 +586,7 @@ class DesktopModeEventLoggerTest : ShellTestCase() { } private companion object { - private const val SESSION_ID = 1 + private const val sessionId = 1 private const val TASK_ID = 1 private const val TASK_UID = 1 private const val TASK_X = 0 @@ -453,23 +611,12 @@ class DesktopModeEventLoggerTest : ShellTestCase() { DISPLAY_AREA, ) - private fun createTaskSizeUpdate( - resizeTrigger: ResizeTrigger = ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, - inputMethod: InputMethod = InputMethod.UNKNOWN_INPUT_METHOD, - ) = TaskSizeUpdate( - resizeTrigger, - inputMethod, - TASK_ID, - TASK_UID, - TASK_HEIGHT, - TASK_WIDTH, - DISPLAY_AREA, - ) - private fun createTaskUpdate( minimizeReason: MinimizeReason? = null, unminimizeReason: UnminimizeReason? = null, - ) = TaskUpdate(TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, - unminimizeReason, TASK_COUNT) + ) = TaskUpdate( + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y, minimizeReason, + unminimizeReason, TASK_COUNT + ) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index e7593b5b9324..f25faa53f356 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -59,8 +59,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions -import junit.framework.Assert.assertNotNull -import junit.framework.Assert.assertNull +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test @@ -142,8 +142,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) + verify(desktopModeEventLogger, never()).logSessionEnter(any()) + verify(desktopModeEventLogger, never()).logTaskAdded(any()) } @Test @@ -228,11 +228,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitToFront_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -241,7 +240,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -259,11 +259,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitChange_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -272,7 +271,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -290,11 +290,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun transitOpen_previousTransitionExitToOverview_logTaskAddedAndEnterReasonOverview() { // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -303,7 +302,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // Enter desktop mode from cancelled recents has no transition. Enter is detected on the // next transition involving freeform windows @@ -324,11 +324,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { // Tests for AppFromOverview precedence in compared to cancelled Overview // previous exit to overview transition - val previousSessionId = 1 // add a freeform task val previousTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(previousTaskInfo) - transitionObserver.setLoggerSessionId(previousSessionId) + transitionObserver.isSessionActive = true val previousTransitionInfo = TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) .addChange(createChange(TRANSIT_TO_BACK, previousTaskInfo)) @@ -337,7 +336,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(previousTransitionInfo) verifyTaskRemovedAndExitLogging( - previousSessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) // TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FREEFORM)) @@ -379,11 +379,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { fun transitBack_previousExitReasonScreenOff_logTaskAddedAndEnterReasonScreenOn() { val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) // Previous Exit reason recorded as Screen Off - val sessionId = 1 transitionObserver.addTaskInfosToCachedMap(freeformTask) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) // Enter desktop through back transition, this happens when user enters after dismissing // keyguard val change = createChange(TRANSIT_TO_FRONT, freeformTask) @@ -399,11 +398,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { fun transitEndDragToDesktop_previousExitReasonScreenOff_logTaskAddedAndEnterReasonAppDrag() { val freeformTask = createTaskInfo(WINDOWING_MODE_FREEFORM) // Previous Exit reason recorded as Screen Off - val sessionId = 1 transitionObserver.addTaskInfosToCachedMap(freeformTask) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true callOnTransitionReady(TransitionInfoBuilder(TRANSIT_SLEEP).build()) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) // Enter desktop through app handle drag. This represents cases where instead of moving to // desktop right after turning the screen on, we move to fullscreen then move another task @@ -419,24 +417,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } @Test - fun transitSleep_logTaskRemovedAndExitReasonScreenOff_sessionIdNull() { - val sessionId = 1 + fun transitSleep_logTaskRemovedAndExitReasonScreenOff() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.SCREEN_OFF, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -444,15 +440,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.DRAG_TO_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -462,16 +457,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { .build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -479,16 +472,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.KEYBOARD_SHORTCUT_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { - val sessionId = 1 + fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // window mode changing from FREEFORM to FULLSCREEN val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) @@ -496,15 +487,14 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.UNKNOWN_EXIT, DEFAULT_TASK_UPDATE) } @Test - fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { - val sessionId = 1 + fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // recents transition val change = createChange(TRANSIT_TO_BACK, createTaskInfo(WINDOWING_MODE_FREEFORM)) @@ -513,31 +503,30 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) } @Test - fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { - val sessionId = 1 + fun transitClose_logTaskRemovedAndExitReasonTaskFinished() { // add a freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task closing val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FULLSCREEN)) val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() callOnTransitionReady(transitionInfo) - verifyTaskRemovedAndExitLogging(sessionId, ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE) + verifyTaskRemovedAndExitLogging(ExitReason.TASK_FINISHED, DEFAULT_TASK_UPDATE) } @Test fun sessionExitByRecents_cancelledAnimation_sessionRestored() { - val sessionId = 1 // add a freeform task to an existing session val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // recents transition sent freeform window to back val change = createChange(TRANSIT_TO_BACK, taskInfo) @@ -546,7 +535,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo1) verifyTaskRemovedAndExitLogging( - sessionId, ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE) + ExitReason.RETURN_HOME_OR_OVERVIEW, DEFAULT_TASK_UPDATE + ) val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() callOnTransitionReady(transitionInfo2) @@ -557,10 +547,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { - val sessionId = 1 // add an existing freeform task transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // new freeform task added val change = createChange(TRANSIT_OPEN, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) @@ -568,18 +557,16 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskAdded(eq(sessionId), - eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + .logTaskAdded(eq(DEFAULT_TASK_UPDATE.copy(instanceId = 2, visibleTaskCount = 2))) + verify(desktopModeEventLogger, never()).logSessionEnter(any()) } @Test fun sessionAlreadyStarted_taskPositionChanged_logsTaskUpdate() { - val sessionId = 1 // add an existing freeform task val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task position changed val newTaskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100) @@ -591,18 +578,17 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), - eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1))) + eq(DEFAULT_TASK_UPDATE.copy(taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 1)) + ) verifyZeroInteractions(desktopModeEventLogger) } @Test fun sessionAlreadyStarted_taskResized_logsTaskUpdate() { - val sessionId = 1 // add an existing freeform task val taskInfo = createTaskInfo(WINDOWING_MODE_FREEFORM) transitionObserver.addTaskInfosToCachedMap(taskInfo) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task resized val newTaskInfo = @@ -618,23 +604,22 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( taskWidth = DEFAULT_TASK_WIDTH + 100, taskHeight = DEFAULT_TASK_HEIGHT - 100, - visibleTaskCount = 1))) + visibleTaskCount = 1)) + ) verifyZeroInteractions(desktopModeEventLogger) } @Test fun sessionAlreadyStarted_multipleTasksUpdated_logsTaskUpdateForCorrectTask() { - val sessionId = 1 // add 2 existing freeform task val taskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM) val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2) transitionObserver.addTaskInfosToCachedMap(taskInfo1) transitionObserver.addTaskInfosToCachedMap(taskInfo2) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // task 1 position update val newTaskInfo1 = createTaskInfo(WINDOWING_MODE_FREEFORM, taskX = DEFAULT_TASK_X + 100) @@ -646,8 +631,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( - taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2))) + eq(DEFAULT_TASK_UPDATE.copy( + taskX = DEFAULT_TASK_X + 100, visibleTaskCount = 2)) + ) verifyZeroInteractions(desktopModeEventLogger) // task 2 resize @@ -666,7 +652,6 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, times(1)) .logTaskInfoChanged( - eq(sessionId), eq( DEFAULT_TASK_UPDATE.copy( instanceId = 2, @@ -679,11 +664,10 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { @Test fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { - val sessionId = 1 // add two existing freeform tasks transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM)) transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) - transitionObserver.setLoggerSessionId(sessionId) + transitionObserver.isSessionActive = true // new freeform task closed val change = createChange(TRANSIT_CLOSE, createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2)) @@ -691,9 +675,11 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { callOnTransitionReady(transitionInfo) verify(desktopModeEventLogger, times(1)) - .logTaskRemoved(eq(sessionId), eq(DEFAULT_TASK_UPDATE.copy( - instanceId = 2, visibleTaskCount = 1))) - verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) + .logTaskRemoved( + eq(DEFAULT_TASK_UPDATE.copy( + instanceId = 2, visibleTaskCount = 1)) + ) + verify(desktopModeEventLogger, never()).logSessionExit(any()) } /** Simulate calling the onTransitionReady() method */ @@ -706,10 +692,9 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } private fun verifyTaskAddedAndEnterLogging(enterReason: EnterReason, taskUpdate: TaskUpdate) { - val sessionId = transitionObserver.getLoggerSessionId() - assertNotNull(sessionId) - verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(sessionId!!), eq(enterReason)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), eq(taskUpdate)) + assertTrue(transitionObserver.isSessionActive) + verify(desktopModeEventLogger, times(1)).logSessionEnter(eq(enterReason)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(taskUpdate)) ExtendedMockito.verify { Trace.setCounter( eq(Trace.TRACE_TAG_WINDOW_MANAGER), @@ -725,14 +710,13 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { } private fun verifyTaskRemovedAndExitLogging( - sessionId: Int, exitReason: ExitReason, taskUpdate: TaskUpdate ) { - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), eq(taskUpdate)) - verify(desktopModeEventLogger, times(1)).logSessionExit(eq(sessionId), eq(exitReason)) + assertFalse(transitionObserver.isSessionActive) + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(taskUpdate)) + verify(desktopModeEventLogger, times(1)).logSessionExit(eq(exitReason)) verifyZeroInteractions(desktopModeEventLogger) - assertNull(transitionObserver.getLoggerSessionId()) } private companion object { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt index 230f7e6912ee..0bd3e083671e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -1,5 +1,6 @@ package com.android.wm.shell.desktopmode +import android.animation.AnimatorTestRule import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD @@ -24,6 +25,7 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS 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 @@ -38,6 +40,7 @@ import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.After import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any @@ -58,6 +61,9 @@ import org.mockito.quality.Strictness @RunWithLooper @RunWith(AndroidTestingRunner::class) class DragToDesktopTransitionHandlerTest : ShellTestCase() { + @JvmField + @Rule + val mAnimatorTestRule = AnimatorTestRule(this) @Mock private lateinit var transitions: Transitions @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @@ -267,16 +273,36 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { } @Test - fun cancelDragToDesktop_startWasReady_cancel() { - startDrag(defaultHandler) + fun cancelDragToDesktop_startWasReady_cancel_merged() { + val startToken = startDrag(defaultHandler) // Then user cancelled after it had already started. - defaultHandler.cancelDragToDesktopTransition( - DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL - ) + val cancelToken = cancelDragToDesktopTransition( + defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL) + defaultHandler.mergeAnimation( + cancelToken, + TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0), + mock<SurfaceControl.Transaction>(), + startToken, + mock<Transitions.TransitionFinishCallback>()) + + // Cancel animation should run since it had already started. + verify(dragAnimator).cancelAnimator() + assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress) + } + + @Test + fun cancelDragToDesktop_startWasReady_cancel_aborted() { + val startToken = startDrag(defaultHandler) + + // Then user cancelled after it had already started. + val cancelToken = cancelDragToDesktopTransition( + defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL) + defaultHandler.onTransitionConsumed(cancelToken, aborted = true, null) // Cancel animation should run since it had already started. verify(dragAnimator).cancelAnimator() + assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress) } @Test @@ -585,6 +611,23 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { return token } + private fun cancelDragToDesktopTransition( + handler: DragToDesktopTransitionHandler, + cancelState: DragToDesktopTransitionHandler.CancelState): IBinder { + val token = mock<IBinder>() + whenever( + transitions.startTransition( + eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), + any(), + eq(handler) + ) + ) + .thenReturn(token) + handler.cancelDragToDesktopTransition(cancelState) + mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS) + return token + } + private fun performEarlyCancel( handler: DragToDesktopTransitionHandler, cancelState: DragToDesktopTransitionHandler.CancelState diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java index 46b60499a01d..eb74218b866a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java @@ -150,7 +150,8 @@ public class SplitDragPolicyTest extends ShellTestCase { mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false); mInsets = Insets.of(0, 0, 0, 0); - mPolicy = spy(new SplitDragPolicy(mContext, mSplitScreenStarter, mFullscreenStarter)); + mPolicy = spy(new SplitDragPolicy(mContext, mSplitScreenStarter, mFullscreenStarter, + mock(DragZoneAnimator.class))); mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY); mLaunchableIntentPendingIntent = mock(PendingIntent.class); when(mLaunchableIntentPendingIntent.getCreatorUserHandle()) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 67eda8bfecd1..a6e33e5e7c29 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -232,17 +232,6 @@ public class StageCoordinatorTests extends ShellTestCase { } @Test - public void testRemoveFromSideStage() { - final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); - - doReturn(false).when(mMainStage).isActive(); - mStageCoordinator.removeFromSideStage(task.taskId); - - verify(mSideStage).removeTask( - eq(task.taskId), any(), any(WindowContainerTransaction.class)); - } - - @Test public void testResolveStartStage_beforeSplitActivated_setsStagePosition() { mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index b7b7d0d35bcf..189684dc391a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -23,10 +23,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -64,8 +63,6 @@ import java.util.Optional; @SmallTest @RunWith(AndroidJUnit4.class) public final class StageTaskListenerTests extends ShellTestCase { - private static final boolean ENABLE_SHELL_TRANSITIONS = - SystemProperties.getBoolean("persist.wm.debug.shell_transit", true); @Mock private ShellTaskOrganizer mTaskOrganizer; @@ -117,20 +114,20 @@ public final class StageTaskListenerTests extends ShellTestCase { public void testRootTaskAppeared() { assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId); verify(mCallbacks).onRootTaskAppeared(); - verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(false)); + verify(mCallbacks, never()).onStageHasChildrenChanged(mStageTaskListener); + verify(mCallbacks, never()).onStageVisibilityChanged(mStageTaskListener); } @Test - public void testChildTaskAppeared() { - // With shell transitions, the transition manages status changes, so skip this test. - assumeFalse(ENABLE_SHELL_TRANSITIONS); - final ActivityManager.RunningTaskInfo childTask = - new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build(); + public void testRootTaskVisible() { + mStageTaskListener.onTaskVanished(mRootTask); + mRootTask = new TestRunningTaskInfoBuilder().setVisible(true).build(); + mRootTask.parentTaskId = INVALID_TASK_ID; + mSurfaceControl = new SurfaceControl.Builder().setName("test").build(); + mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl); - mStageTaskListener.onTaskAppeared(childTask, mSurfaceControl); + verify(mCallbacks).onStageVisibilityChanged(mStageTaskListener); - assertThat(mStageTaskListener.mChildrenTaskInfo.contains(childTask.taskId)).isTrue(); - verify(mCallbacks).onStatusChanged(eq(mRootTask.isVisible), eq(true)); } @Test(expected = IllegalArgumentException.class) @@ -140,29 +137,13 @@ public final class StageTaskListenerTests extends ShellTestCase { } @Test - public void testTaskVanished() { - // With shell transitions, the transition manages status changes, so skip this test. - assumeFalse(ENABLE_SHELL_TRANSITIONS); - final ActivityManager.RunningTaskInfo childTask = - new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build(); - mStageTaskListener.mRootTaskInfo = mRootTask; - mStageTaskListener.mChildrenTaskInfo.put(childTask.taskId, childTask); - - mStageTaskListener.onTaskVanished(childTask); - verify(mCallbacks, times(2)).onStatusChanged(eq(mRootTask.isVisible), eq(false)); - - mStageTaskListener.onTaskVanished(mRootTask); - verify(mCallbacks).onRootTaskVanished(); - } - - @Test public void testTaskInfoChanged_notSupportsMultiWindow() { final ActivityManager.RunningTaskInfo childTask = new TestRunningTaskInfoBuilder().setParentTaskId(mRootTask.taskId).build(); childTask.supportsMultiWindow = false; mStageTaskListener.onTaskInfoChanged(childTask); - verify(mCallbacks).onNoLongerSupportMultiWindow(childTask); + verify(mCallbacks).onNoLongerSupportMultiWindow(mStageTaskListener, childTask); } @Test diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 23cd3ce965ff..266c23631800 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -501,6 +501,13 @@ genrule { ], } +genrule { + name: "statslog-hwui-java-gen", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --java $(out) --module hwui --javaPackage com.android.os.coregraphics --javaClass HwuiStatsLog", + out: ["com/android/os/coregraphics/HwuiStatsLog.java"], +} + // ------------------------ // library // ------------------------ diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index cdb517b3fd3e..536afd4f712e 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -51,6 +51,7 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.Overridable; import android.compat.annotation.UnsupportedAppUsage; +import android.content.AttributionSource; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -1918,12 +1919,18 @@ public class AudioManager { @Deprecated public void setSpeakerphoneOn(boolean on) { final IAudioService service = getService(); try { - service.setSpeakerphoneOn(mICallBack, on); + service.setSpeakerphoneOn(mICallBack, on, getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + private AttributionSource getAttributionSource() { + Context context = getContext(); + return (context != null) + ? context.getAttributionSource() : AttributionSource.myAttributionSource(); + } + /** * Checks whether the speakerphone is on or off. * @@ -3089,7 +3096,8 @@ public class AudioManager { final IAudioService service = getService(); try { service.startBluetoothSco(mICallBack, - getContext().getApplicationInfo().targetSdkVersion); + getContext().getApplicationInfo().targetSdkVersion, + getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3114,7 +3122,7 @@ public class AudioManager { public void startBluetoothScoVirtualCall() { final IAudioService service = getService(); try { - service.startBluetoothScoVirtualCall(mICallBack); + service.startBluetoothScoVirtualCall(mICallBack, getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -3134,7 +3142,7 @@ public class AudioManager { @Deprecated public void stopBluetoothSco() { final IAudioService service = getService(); try { - service.stopBluetoothSco(mICallBack); + service.stopBluetoothSco(mICallBack, getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -9023,7 +9031,8 @@ public class AudioManager { Log.w(TAG, "setCommunicationDevice: device not found: " + device); return false; } - return getService().setCommunicationDevice(mICallBack, device.getId()); + return getService().setCommunicationDevice(mICallBack, device.getId(), + getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -9035,7 +9044,7 @@ public class AudioManager { */ public void clearCommunicationDevice() { try { - getService().setCommunicationDevice(mICallBack, 0); + getService().setCommunicationDevice(mICallBack, 0, getAttributionSource()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 4d6ddfdaef2e..3cd5f5266ef2 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -18,7 +18,9 @@ package android.media; import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL; import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE; +import static android.media.audio.Flags.FLAG_MUTED_BY_PORT_VOLUME_API; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -291,12 +293,24 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_VOLUME_SHAPER = (1 << 5); + /** + * @hide + * Flag used when muted by the track's port volume. + * + * <p>Note: this will replace the stream volume mute when using the AudioFlinger port volume + * APIs + */ + @SystemApi + @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API) + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public static final int MUTED_BY_PORT_VOLUME = (1 << 6); /** @hide */ @IntDef( flag = true, value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED, - MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER}) + MUTED_BY_APP_OPS, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER, + MUTED_BY_PORT_VOLUME}) @Retention(RetentionPolicy.SOURCE) public @interface PlayerMuteEvent { } @@ -858,6 +872,9 @@ public final class AudioPlaybackConfiguration implements Parcelable { if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) { apcToString.append("volumeShaper "); } + if ((mMutedState & MUTED_BY_PORT_VOLUME) != 0) { + apcToString.append("portVolume "); + } } apcToString.append(" ").append(mFormatInfo); } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index a8b863bc67f9..bf09cb07a8ed 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -1698,12 +1698,12 @@ public class AudioSystem } /** @hide Wrapper for native methods called from AudioService */ - public static int setStreamVolumeIndexAS(int stream, int index, int device) { + public static int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) { if (DEBUG_VOLUME) { Log.i(TAG, "setStreamVolumeIndex: " + STREAM_NAMES[stream] - + " dev=" + Integer.toHexString(device) + " idx=" + index); + + " dev=" + Integer.toHexString(device) + " idx=" + index + " muted=" + muted); } - return setStreamVolumeIndex(stream, index, device); + return setStreamVolumeIndex(stream, index, muted, device); } // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t @@ -1774,7 +1774,8 @@ public class AudioSystem @UnsupportedAppUsage public static native int initStreamVolume(int stream, int indexMin, int indexMax); @UnsupportedAppUsage - private static native int setStreamVolumeIndex(int stream, int index, int device); + private static native int setStreamVolumeIndex(int stream, int index, boolean muted, + int device); /** @hide */ public static native int getStreamVolumeIndex(int stream, int device); /** @@ -1787,7 +1788,7 @@ public class AudioSystem * @return command completion status. */ public static native int setVolumeIndexForAttributes(@NonNull AudioAttributes attributes, - int index, int device); + int index, boolean muted, int device); /** * @hide * get the volume index for the given {@link AudioAttributes}. diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 8394daf5966c..02ca307eb2ee 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -234,7 +234,7 @@ interface IAudioService { int getEncodedSurroundMode(int targetSdkVersion); - void setSpeakerphoneOn(IBinder cb, boolean on); + void setSpeakerphoneOn(IBinder cb, boolean on, in AttributionSource attributionSource); boolean isSpeakerphoneOn(); @@ -263,9 +263,10 @@ interface IAudioService { int getCurrentAudioFocus(); - void startBluetoothSco(IBinder cb, int targetSdkVersion); - void startBluetoothScoVirtualCall(IBinder cb); - void stopBluetoothSco(IBinder cb); + void startBluetoothSco(IBinder cb, int targetSdkVersion, + in AttributionSource attributionSource); + void startBluetoothScoVirtualCall(IBinder cb, in AttributionSource attributionSource); + void stopBluetoothSco(IBinder cb, in AttributionSource attributionSource); void forceVolumeControlStream(int streamType, IBinder cb); @@ -542,7 +543,7 @@ interface IAudioService { int[] getAvailableCommunicationDeviceIds(); - boolean setCommunicationDevice(IBinder cb, int portId); + boolean setCommunicationDevice(IBinder cb, int portId, in AttributionSource attributionSource); int getCommunicationDevice(); diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 8b04644a2989..b022ea11d848 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -134,7 +134,7 @@ public final class MediaCas implements AutoCloseable { private int mCasSystemId; private int mUserId; private TunerResourceManager mTunerResourceManager = null; - private final Map<Session, Integer> mSessionMap = new HashMap<>(); + private final Map<Session, Long> mSessionMap = new HashMap<>(); /** * Scrambling modes used to open cas sessions. @@ -1150,10 +1150,10 @@ public final class MediaCas implements AutoCloseable { } } - private int getSessionResourceHandle() throws MediaCasException { + private long getSessionResourceHandle() throws MediaCasException { validateInternalStates(); - int[] sessionResourceHandle = new int[1]; + long[] sessionResourceHandle = new long[1]; sessionResourceHandle[0] = -1; if (mTunerResourceManager != null) { CasSessionRequest casSessionRequest = new CasSessionRequest(); @@ -1168,8 +1168,7 @@ public final class MediaCas implements AutoCloseable { return sessionResourceHandle[0]; } - private void addSessionToResourceMap(Session session, int sessionResourceHandle) { - + private void addSessionToResourceMap(Session session, long sessionResourceHandle) { if (sessionResourceHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { synchronized (mSessionMap) { mSessionMap.put(session, sessionResourceHandle); @@ -1202,13 +1201,14 @@ public final class MediaCas implements AutoCloseable { * @throws MediaCasStateException for CAS-specific state exceptions. */ public Session openSession() throws MediaCasException { - int sessionResourceHandle = getSessionResourceHandle(); + long sessionResourceHandle = getSessionResourceHandle(); try { if (mICas != null) { try { byte[] sessionId = mICas.openSessionDefault(); Session session = createFromSessionId(sessionId); + addSessionToResourceMap(session, sessionResourceHandle); Log.d(TAG, "Write Stats Log for succeed to Open Session."); FrameworkStatsLog.write( FrameworkStatsLog.TV_CAS_SESSION_OPEN_STATUS, @@ -1262,7 +1262,7 @@ public final class MediaCas implements AutoCloseable { @Nullable public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode) throws MediaCasException { - int sessionResourceHandle = getSessionResourceHandle(); + long sessionResourceHandle = getSessionResourceHandle(); if (mICas != null) { try { diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig index 10423b9c1f0f..d49f7dd92bf7 100644 --- a/media/java/android/media/tv/flags/media_tv.aconfig +++ b/media/java/android/media/tv/flags/media_tv.aconfig @@ -64,3 +64,11 @@ flag { description: "Media Quality V1.0 APIs for Android W" bug: "348412562" } + +flag { + name: "tif_extension_standardization" + is_exported: true + namespace: "media_tv" + description: "Standardize AIDL Extension Interface of TIS" + bug: "330366987" +}
\ No newline at end of file diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 92f6eaf33c88..cdf50ec963d8 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -295,13 +295,13 @@ public class Tuner implements AutoCloseable { private EventHandler mHandler; @Nullable private FrontendInfo mFrontendInfo; - private Integer mFrontendHandle; + private Long mFrontendHandle; private Tuner mFeOwnerTuner = null; private int mFrontendType = FrontendSettings.TYPE_UNDEFINED; private Integer mDesiredFrontendId = null; private int mUserId; private Lnb mLnb; - private Integer mLnbHandle; + private Long mLnbHandle; @Nullable private OnTuneEventListener mOnTuneEventListener; @Nullable @@ -324,10 +324,10 @@ public class Tuner implements AutoCloseable { private final ReentrantLock mDemuxLock = new ReentrantLock(); private int mRequestedCiCamId; - private Integer mDemuxHandle; - private Integer mFrontendCiCamHandle; + private Long mDemuxHandle; + private Long mFrontendCiCamHandle; private Integer mFrontendCiCamId; - private Map<Integer, WeakReference<Descrambler>> mDescramblers = new HashMap<>(); + private Map<Long, WeakReference<Descrambler>> mDescramblers = new HashMap<>(); private List<WeakReference<Filter>> mFilters = new ArrayList<WeakReference<Filter>>(); private final TunerResourceManager.ResourcesReclaimListener mResourceListener = @@ -949,7 +949,7 @@ public class Tuner implements AutoCloseable { private void releaseDescramblers() { synchronized (mDescramblers) { if (!mDescramblers.isEmpty()) { - for (Map.Entry<Integer, WeakReference<Descrambler>> d : mDescramblers.entrySet()) { + for (Map.Entry<Long, WeakReference<Descrambler>> d : mDescramblers.entrySet()) { Descrambler descrambler = d.getValue().get(); if (descrambler != null) { descrambler.close(); @@ -1010,7 +1010,7 @@ public class Tuner implements AutoCloseable { /** * Native method to open frontend of the given ID. */ - private native Frontend nativeOpenFrontendByHandle(int handle); + private native Frontend nativeOpenFrontendByHandle(long handle); private native int nativeShareFrontend(int id); private native int nativeUnshareFrontend(); private native void nativeRegisterFeCbListener(long nativeContext); @@ -1039,21 +1039,21 @@ public class Tuner implements AutoCloseable { private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber); private native int nativeGetMaxNumberOfFrontends(int frontendType); private native int nativeRemoveOutputPid(int pid); - private native Lnb nativeOpenLnbByHandle(int handle); + private native Lnb nativeOpenLnbByHandle(long handle); private native Lnb nativeOpenLnbByName(String name); private native FrontendStatusReadiness[] nativeGetFrontendStatusReadiness(int[] statusTypes); - private native Descrambler nativeOpenDescramblerByHandle(int handle); - private native int nativeOpenDemuxByhandle(int handle); + private native Descrambler nativeOpenDescramblerByHandle(long handle); + private native int nativeOpenDemuxByhandle(long handle); private native DvrRecorder nativeOpenDvrRecorder(long bufferSize); private native DvrPlayback nativeOpenDvrPlayback(long bufferSize); private native DemuxCapabilities nativeGetDemuxCapabilities(); - private native DemuxInfo nativeGetDemuxInfo(int demuxHandle); + private native DemuxInfo nativeGetDemuxInfo(long demuxHandle); - private native int nativeCloseDemux(int handle); - private native int nativeCloseFrontend(int handle); + private native int nativeCloseDemux(long handle); + private native int nativeCloseFrontend(long handle); private native int nativeClose(); private static native SharedFilter nativeOpenSharedFilter(String token); @@ -1371,7 +1371,7 @@ public class Tuner implements AutoCloseable { } private boolean requestFrontend() { - int[] feHandle = new int[1]; + long[] feHandle = new long[1]; boolean granted = false; try { TunerFrontendRequest request = new TunerFrontendRequest(); @@ -2379,7 +2379,7 @@ public class Tuner implements AutoCloseable { } private boolean requestLnb() { - int[] lnbHandle = new int[1]; + long[] lnbHandle = new long[1]; TunerLnbRequest request = new TunerLnbRequest(); request.clientId = mClientId; boolean granted = mTunerResourceManager.requestLnb(request, lnbHandle); @@ -2709,7 +2709,7 @@ public class Tuner implements AutoCloseable { } private boolean requestDemux() { - int[] demuxHandle = new int[1]; + long[] demuxHandle = new long[1]; TunerDemuxRequest request = new TunerDemuxRequest(); request.clientId = mClientId; request.desiredFilterTypes = mDesiredDemuxInfo.getFilterTypes(); @@ -2722,14 +2722,14 @@ public class Tuner implements AutoCloseable { } private Descrambler requestDescrambler() { - int[] descramblerHandle = new int[1]; + long[] descramblerHandle = new long[1]; TunerDescramblerRequest request = new TunerDescramblerRequest(); request.clientId = mClientId; boolean granted = mTunerResourceManager.requestDescrambler(request, descramblerHandle); if (!granted) { return null; } - int handle = descramblerHandle[0]; + long handle = descramblerHandle[0]; Descrambler descrambler = nativeOpenDescramblerByHandle(handle); if (descrambler != null) { synchronized (mDescramblers) { @@ -2743,7 +2743,7 @@ public class Tuner implements AutoCloseable { } private boolean requestFrontendCiCam(int ciCamId) { - int[] ciCamHandle = new int[1]; + long[] ciCamHandle = new long[1]; TunerCiCamRequest request = new TunerCiCamRequest(); request.clientId = mClientId; request.ciCamId = ciCamId; diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index d268aeba8011..bb581ebe1778 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -66,7 +66,7 @@ public class TunerResourceManager { private static final String TAG = "TunerResourceManager"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - public static final int INVALID_RESOURCE_HANDLE = -1; + public static final long INVALID_RESOURCE_HANDLE = -1; public static final int INVALID_OWNER_ID = -1; /** * Tuner resource type to help generate resource handle @@ -275,7 +275,7 @@ public class TunerResourceManager { * Updates the current TRM of the TunerHAL Frontend information. * * <p><strong>Note:</strong> This update must happen before the first - * {@link #requestFrontend(TunerFrontendRequest, int[])} and + * {@link #requestFrontend(TunerFrontendRequest, long[])} and * {@link #releaseFrontend(int, int)} call. * * @param infos an array of the available {@link TunerFrontendInfo} information. @@ -331,7 +331,7 @@ public class TunerResourceManager { * * @param lnbIds ids of the updating lnbs. */ - public void setLnbInfoList(int[] lnbIds) { + public void setLnbInfoList(long[] lnbIds) { try { mService.setLnbInfoList(lnbIds); } catch (RemoteException e) { @@ -406,8 +406,8 @@ public class TunerResourceManager { * * @return true if there is frontend granted. */ - public boolean requestFrontend(@NonNull TunerFrontendRequest request, - @Nullable int[] frontendHandle) { + public boolean requestFrontend( + @NonNull TunerFrontendRequest request, @Nullable long[] frontendHandle) { boolean result = false; try { result = mService.requestFrontend(request, frontendHandle); @@ -511,7 +511,7 @@ public class TunerResourceManager { * * @return true if there is Demux granted. */ - public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull int[] demuxHandle) { + public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull long[] demuxHandle) { boolean result = false; try { result = mService.requestDemux(request, demuxHandle); @@ -544,8 +544,8 @@ public class TunerResourceManager { * * @return true if there is Descrambler granted. */ - public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, - @NonNull int[] descramblerHandle) { + public boolean requestDescrambler( + @NonNull TunerDescramblerRequest request, @NonNull long[] descramblerHandle) { boolean result = false; try { result = mService.requestDescrambler(request, descramblerHandle); @@ -577,8 +577,8 @@ public class TunerResourceManager { * * @return true if there is CAS session granted. */ - public boolean requestCasSession(@NonNull CasSessionRequest request, - @NonNull int[] casSessionHandle) { + public boolean requestCasSession( + @NonNull CasSessionRequest request, @NonNull long[] casSessionHandle) { boolean result = false; try { result = mService.requestCasSession(request, casSessionHandle); @@ -610,7 +610,7 @@ public class TunerResourceManager { * * @return true if there is ciCam granted. */ - public boolean requestCiCam(TunerCiCamRequest request, int[] ciCamHandle) { + public boolean requestCiCam(TunerCiCamRequest request, long[] ciCamHandle) { boolean result = false; try { result = mService.requestCiCam(request, ciCamHandle); @@ -635,7 +635,7 @@ public class TunerResourceManager { * <li>If no Lnb system can be granted, the API would return false. * <ul> * - * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this request. + * <p><strong>Note:</strong> {@link #setLnbInfoList(long[])} must be called before this request. * * @param request {@link TunerLnbRequest} information of the current request. * @param lnbHandle a one-element array to return the granted Lnb handle. @@ -643,7 +643,7 @@ public class TunerResourceManager { * * @return true if there is Lnb granted. */ - public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) { + public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull long[] lnbHandle) { boolean result = false; try { result = mService.requestLnb(request, lnbHandle); @@ -664,7 +664,7 @@ public class TunerResourceManager { * @param frontendHandle the handle of the released frontend. * @param clientId the id of the client that is releasing the frontend. */ - public void releaseFrontend(int frontendHandle, int clientId) { + public void releaseFrontend(long frontendHandle, int clientId) { try { mService.releaseFrontend(frontendHandle, clientId); } catch (RemoteException e) { @@ -680,7 +680,7 @@ public class TunerResourceManager { * @param demuxHandle the handle of the released Tuner Demux. * @param clientId the id of the client that is releasing the demux. */ - public void releaseDemux(int demuxHandle, int clientId) { + public void releaseDemux(long demuxHandle, int clientId) { try { mService.releaseDemux(demuxHandle, clientId); } catch (RemoteException e) { @@ -696,7 +696,7 @@ public class TunerResourceManager { * @param descramblerHandle the handle of the released Tuner Descrambler. * @param clientId the id of the client that is releasing the descrambler. */ - public void releaseDescrambler(int descramblerHandle, int clientId) { + public void releaseDescrambler(long descramblerHandle, int clientId) { try { mService.releaseDescrambler(descramblerHandle, clientId); } catch (RemoteException e) { @@ -715,7 +715,7 @@ public class TunerResourceManager { * @param casSessionHandle the handle of the released CAS session. * @param clientId the id of the client that is releasing the cas session. */ - public void releaseCasSession(int casSessionHandle, int clientId) { + public void releaseCasSession(long casSessionHandle, int clientId) { try { mService.releaseCasSession(casSessionHandle, clientId); } catch (RemoteException e) { @@ -734,7 +734,7 @@ public class TunerResourceManager { * @param ciCamHandle the handle of the releasing CiCam. * @param clientId the id of the client that is releasing the CiCam. */ - public void releaseCiCam(int ciCamHandle, int clientId) { + public void releaseCiCam(long ciCamHandle, int clientId) { try { mService.releaseCiCam(ciCamHandle, clientId); } catch (RemoteException e) { @@ -747,12 +747,12 @@ public class TunerResourceManager { * * <p>Client must call this whenever it releases an Lnb. * - * <p><strong>Note:</strong> {@link #setLnbInfoList(int[])} must be called before this release. + * <p><strong>Note:</strong> {@link #setLnbInfoList(long[])} must be called before this release. * * @param lnbHandle the handle of the released Tuner Lnb. * @param clientId the id of the client that is releasing the lnb. */ - public void releaseLnb(int lnbHandle, int clientId) { + public void releaseLnb(long lnbHandle, int clientId) { try { mService.releaseLnb(lnbHandle, clientId); } catch (RemoteException e) { diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 539969762a82..109c791c1748 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -149,7 +149,7 @@ interface ITunerResourceManager { * * @param lnbIds ids of the updating lnbs. */ - void setLnbInfoList(in int[] lnbIds); + void setLnbInfoList(in long[] lnbIds); /* * This API is used by the Tuner framework to request a frontend from the TunerHAL. @@ -185,7 +185,7 @@ interface ITunerResourceManager { * * @return true if there is frontend granted. */ - boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendHandle); + boolean requestFrontend(in TunerFrontendRequest request, out long[] frontendHandle); /* * Sets the maximum usable frontends number of a given frontend type. It is used to enable or @@ -253,7 +253,7 @@ interface ITunerResourceManager { * * @return true if there is demux granted. */ - boolean requestDemux(in TunerDemuxRequest request, out int[] demuxHandle); + boolean requestDemux(in TunerDemuxRequest request, out long[] demuxHandle); /* * This API is used by the Tuner framework to request an available descrambler from the @@ -277,7 +277,7 @@ interface ITunerResourceManager { * * @return true if there is Descrambler granted. */ - boolean requestDescrambler(in TunerDescramblerRequest request, out int[] descramblerHandle); + boolean requestDescrambler(in TunerDescramblerRequest request, out long[] descramblerHandle); /* * This API is used by the Tuner framework to request an available Cas session. This session @@ -303,7 +303,7 @@ interface ITunerResourceManager { * * @return true if there is CAS session granted. */ - boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle); + boolean requestCasSession(in CasSessionRequest request, out long[] casSessionHandle); /* * This API is used by the Tuner framework to request an available CuCam. @@ -328,7 +328,7 @@ interface ITunerResourceManager { * * @return true if there is CiCam granted. */ - boolean requestCiCam(in TunerCiCamRequest request, out int[] ciCamHandle); + boolean requestCiCam(in TunerCiCamRequest request, out long[] ciCamHandle); /* * This API is used by the Tuner framework to request an available Lnb from the TunerHAL. @@ -352,7 +352,7 @@ interface ITunerResourceManager { * * @return true if there is Lnb granted. */ - boolean requestLnb(in TunerLnbRequest request, out int[] lnbHandle); + boolean requestLnb(in TunerLnbRequest request, out long[] lnbHandle); /* * Notifies the TRM that the given frontend has been released. @@ -365,7 +365,7 @@ interface ITunerResourceManager { * @param frontendHandle the handle of the released frontend. * @param clientId the id of the client that is releasing the frontend. */ - void releaseFrontend(in int frontendHandle, int clientId); + void releaseFrontend(in long frontendHandle, int clientId); /* * Notifies the TRM that the Demux with the given handle was released. @@ -375,7 +375,7 @@ interface ITunerResourceManager { * @param demuxHandle the handle of the released Tuner Demux. * @param clientId the id of the client that is releasing the demux. */ - void releaseDemux(in int demuxHandle, int clientId); + void releaseDemux(in long demuxHandle, int clientId); /* * Notifies the TRM that the Descrambler with the given handle was released. @@ -385,7 +385,7 @@ interface ITunerResourceManager { * @param descramblerHandle the handle of the released Tuner Descrambler. * @param clientId the id of the client that is releasing the descrambler. */ - void releaseDescrambler(in int descramblerHandle, int clientId); + void releaseDescrambler(in long descramblerHandle, int clientId); /* * Notifies the TRM that the given Cas session has been released. @@ -397,7 +397,7 @@ interface ITunerResourceManager { * @param casSessionHandle the handle of the released CAS session. * @param clientId the id of the client that is releasing the cas session. */ - void releaseCasSession(in int casSessionHandle, int clientId); + void releaseCasSession(in long casSessionHandle, int clientId); /** * Notifies the TRM that the given CiCam has been released. @@ -410,7 +410,7 @@ interface ITunerResourceManager { * @param ciCamHandle the handle of the releasing CiCam. * @param clientId the id of the client that is releasing the CiCam. */ - void releaseCiCam(in int ciCamHandle, int clientId); + void releaseCiCam(in long ciCamHandle, int clientId); /* * Notifies the TRM that the Lnb with the given handle was released. @@ -422,7 +422,7 @@ interface ITunerResourceManager { * @param lnbHandle the handle of the released Tuner Lnb. * @param clientId the id of the client that is releasing the lnb. */ - void releaseLnb(in int lnbHandle, int clientId); + void releaseLnb(in long lnbHandle, int clientId); /* * Compare two clients' priority. diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl index c14caf50fa14..7984c38d33ab 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerDemuxInfo.aidl @@ -26,7 +26,7 @@ parcelable TunerDemuxInfo { /** * Demux handle */ - int handle; + long handle; /** * Supported filter types (defined in {@link android.media.tv.tuner.filter.Filter}) diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl index 8981ce00d509..274367e9aeb6 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendInfo.aidl @@ -26,7 +26,7 @@ parcelable TunerFrontendInfo { /** * Frontend Handle */ - int handle; + long handle; /** * Frontend Type diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 49e794116011..9e1e2c39ee5b 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -1452,7 +1452,7 @@ jobject JTuner::getFrontendIds() { return obj; } -jobject JTuner::openFrontendByHandle(int feHandle) { +jobject JTuner::openFrontendByHandle(jlong feHandle) { // TODO: Handle reopening frontend with different handle sp<FrontendClient> feClient = sTunerClient->openFrontend(feHandle); if (feClient == nullptr) { @@ -1828,7 +1828,7 @@ jobjectArray JTuner::getFrontendStatusReadiness(jintArray types) { return valObj; } -jobject JTuner::openLnbByHandle(int handle) { +jobject JTuner::openLnbByHandle(jlong handle) { if (sTunerClient == nullptr) { return nullptr; } @@ -1837,7 +1837,7 @@ jobject JTuner::openLnbByHandle(int handle) { sp<LnbClientCallbackImpl> callback = new LnbClientCallbackImpl(); lnbClient = sTunerClient->openLnb(handle); if (lnbClient == nullptr) { - ALOGD("Failed to open lnb, handle = %d", handle); + ALOGD("Failed to open lnb, handle = %s", std::to_string(handle).c_str()); return nullptr; } @@ -1951,7 +1951,7 @@ int JTuner::setLna(bool enable) { return (int)result; } -Result JTuner::openDemux(int handle) { +Result JTuner::openDemux(jlong handle) { if (sTunerClient == nullptr) { return Result::NOT_INITIALIZED; } @@ -2219,7 +2219,7 @@ jobject JTuner::getDemuxCaps() { numBytesInSectionFilter, filterCaps, filterCapsList, linkCaps, bTimeFilter); } -jobject JTuner::getDemuxInfo(int handle) { +jobject JTuner::getDemuxInfo(jlong handle) { if (sTunerClient == nullptr) { ALOGE("tuner is not initialized"); return nullptr; @@ -3772,8 +3772,8 @@ static jobject android_media_tv_Tuner_get_frontend_ids(JNIEnv *env, jobject thiz return tuner->getFrontendIds(); } -static jobject android_media_tv_Tuner_open_frontend_by_handle( - JNIEnv *env, jobject thiz, jint handle) { +static jobject android_media_tv_Tuner_open_frontend_by_handle(JNIEnv *env, jobject thiz, + jlong handle) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->openFrontendByHandle(handle); } @@ -3905,7 +3905,7 @@ static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thi return tuner->getFrontendInfo(id); } -static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jint handle) { +static jobject android_media_tv_Tuner_open_lnb_by_handle(JNIEnv *env, jobject thiz, jlong handle) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->openLnbByHandle(handle); } @@ -4626,7 +4626,7 @@ static int android_media_tv_Tuner_time_filter_close(JNIEnv *env, jobject filter) return (int)r; } -static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jint) { +static jobject android_media_tv_Tuner_open_descrambler(JNIEnv *env, jobject thiz, jlong) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->openDescrambler(); } @@ -4694,12 +4694,12 @@ static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) return tuner->getDemuxCaps(); } -static jobject android_media_tv_Tuner_get_demux_info(JNIEnv* env, jobject thiz, jint handle) { +static jobject android_media_tv_Tuner_get_demux_info(JNIEnv *env, jobject thiz, jlong handle) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->getDemuxInfo(handle); } -static jint android_media_tv_Tuner_open_demux(JNIEnv* env, jobject thiz, jint handle) { +static jint android_media_tv_Tuner_open_demux(JNIEnv *env, jobject thiz, jlong handle) { sp<JTuner> tuner = getTuner(env, thiz); return (jint)tuner->openDemux(handle); } @@ -4710,7 +4710,7 @@ static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) { return (jint)tuner->close(); } -static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /* handle */) { +static jint android_media_tv_Tuner_close_demux(JNIEnv *env, jobject thiz, jlong /* handle */) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->closeDemux(); } @@ -4770,7 +4770,7 @@ static jobjectArray android_media_tv_Tuner_get_frontend_status_readiness(JNIEnv return tuner->getFrontendStatusReadiness(types); } -static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) { +static jint android_media_tv_Tuner_close_frontend(JNIEnv *env, jobject thiz, jlong /* handle */) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->closeFrontend(); } @@ -5039,7 +5039,7 @@ static const JNINativeMethod gTunerMethods[] = { { "nativeGetTunerVersion", "()I", (void *)android_media_tv_Tuner_native_get_tuner_version }, { "nativeGetFrontendIds", "()Ljava/util/List;", (void *)android_media_tv_Tuner_get_frontend_ids }, - { "nativeOpenFrontendByHandle", "(I)Landroid/media/tv/tuner/Tuner$Frontend;", + { "nativeOpenFrontendByHandle", "(J)Landroid/media/tv/tuner/Tuner$Frontend;", (void *)android_media_tv_Tuner_open_frontend_by_handle }, { "nativeShareFrontend", "(I)I", (void *)android_media_tv_Tuner_share_frontend }, @@ -5078,11 +5078,11 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_open_filter }, { "nativeOpenTimeFilter", "()Landroid/media/tv/tuner/filter/TimeFilter;", (void *)android_media_tv_Tuner_open_time_filter }, - { "nativeOpenLnbByHandle", "(I)Landroid/media/tv/tuner/Lnb;", + { "nativeOpenLnbByHandle", "(J)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_handle }, { "nativeOpenLnbByName", "(Ljava/lang/String;)Landroid/media/tv/tuner/Lnb;", (void *)android_media_tv_Tuner_open_lnb_by_name }, - { "nativeOpenDescramblerByHandle", "(I)Landroid/media/tv/tuner/Descrambler;", + { "nativeOpenDescramblerByHandle", "(J)Landroid/media/tv/tuner/Descrambler;", (void *)android_media_tv_Tuner_open_descrambler }, { "nativeOpenDvrRecorder", "(J)Landroid/media/tv/tuner/dvr/DvrRecorder;", (void *)android_media_tv_Tuner_open_dvr_recorder }, @@ -5090,12 +5090,12 @@ static const JNINativeMethod gTunerMethods[] = { (void *)android_media_tv_Tuner_open_dvr_playback }, { "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;", (void *)android_media_tv_Tuner_get_demux_caps }, - { "nativeGetDemuxInfo", "(I)Landroid/media/tv/tuner/DemuxInfo;", + { "nativeGetDemuxInfo", "(J)Landroid/media/tv/tuner/DemuxInfo;", (void *)android_media_tv_Tuner_get_demux_info }, - { "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux }, + { "nativeOpenDemuxByhandle", "(J)I", (void *)android_media_tv_Tuner_open_demux }, { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner }, - { "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend }, - { "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux }, + { "nativeCloseFrontend", "(J)I", (void *)android_media_tv_Tuner_close_frontend }, + { "nativeCloseDemux", "(J)I", (void *)android_media_tv_Tuner_close_demux }, { "nativeOpenSharedFilter", "(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;", (void *)android_media_tv_Tuner_open_shared_filter}, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 3de3ab91326c..7af2cd7bd5b7 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -206,7 +206,7 @@ struct JTuner : public RefBase { int disconnectCiCam(); int unlinkCiCam(jint id); jobject getFrontendIds(); - jobject openFrontendByHandle(int feHandle); + jobject openFrontendByHandle(jlong feHandle); int shareFrontend(int feId); int unshareFrontend(); void registerFeCbListener(JTuner* jtuner); @@ -221,16 +221,16 @@ struct JTuner : public RefBase { int setLnb(sp<LnbClient> lnbClient); bool isLnaSupported(); int setLna(bool enable); - jobject openLnbByHandle(int handle); + jobject openLnbByHandle(jlong handle); jobject openLnbByName(jstring name); jobject openFilter(DemuxFilterType type, int bufferSize); jobject openTimeFilter(); jobject openDescrambler(); jobject openDvr(DvrType type, jlong bufferSize); jobject getDemuxCaps(); - jobject getDemuxInfo(int handle); + jobject getDemuxInfo(jlong handle); jobject getFrontendStatus(jintArray types); - Result openDemux(int handle); + Result openDemux(jlong handle); jint close(); jint closeFrontend(); jint closeDemux(); diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp index ea623d97a394..0097710f5c64 100644 --- a/media/jni/tuner/TunerClient.cpp +++ b/media/jni/tuner/TunerClient.cpp @@ -56,7 +56,7 @@ vector<int32_t> TunerClient::getFrontendIds() { return ids; } -sp<FrontendClient> TunerClient::openFrontend(int32_t frontendHandle) { +sp<FrontendClient> TunerClient::openFrontend(int64_t frontendHandle) { if (mTunerService != nullptr) { shared_ptr<ITunerFrontend> tunerFrontend; Status s = mTunerService->openFrontend(frontendHandle, &tunerFrontend); @@ -94,7 +94,7 @@ shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int32_t id) { return nullptr; } -sp<DemuxClient> TunerClient::openDemux(int32_t demuxHandle) { +sp<DemuxClient> TunerClient::openDemux(int64_t demuxHandle) { if (mTunerService != nullptr) { shared_ptr<ITunerDemux> tunerDemux; Status s = mTunerService->openDemux(demuxHandle, &tunerDemux); @@ -107,7 +107,7 @@ sp<DemuxClient> TunerClient::openDemux(int32_t demuxHandle) { return nullptr; } -shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int32_t demuxHandle) { +shared_ptr<DemuxInfo> TunerClient::getDemuxInfo(int64_t demuxHandle) { if (mTunerService != nullptr) { DemuxInfo aidlDemuxInfo; Status s = mTunerService->getDemuxInfo(demuxHandle, &aidlDemuxInfo); @@ -141,7 +141,7 @@ shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() { return nullptr; } -sp<DescramblerClient> TunerClient::openDescrambler(int32_t descramblerHandle) { +sp<DescramblerClient> TunerClient::openDescrambler(int64_t descramblerHandle) { if (mTunerService != nullptr) { shared_ptr<ITunerDescrambler> tunerDescrambler; Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler); @@ -154,7 +154,7 @@ sp<DescramblerClient> TunerClient::openDescrambler(int32_t descramblerHandle) { return nullptr; } -sp<LnbClient> TunerClient::openLnb(int32_t lnbHandle) { +sp<LnbClient> TunerClient::openLnb(int64_t lnbHandle) { if (mTunerService != nullptr) { shared_ptr<ITunerLnb> tunerLnb; Status s = mTunerService->openLnb(lnbHandle, &tunerLnb); diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h index 6ab120b56d97..a348586454b8 100644 --- a/media/jni/tuner/TunerClient.h +++ b/media/jni/tuner/TunerClient.h @@ -65,7 +65,7 @@ public: * @param frontendHandle the handle of the frontend granted by TRM. * @return a newly created FrontendClient interface. */ - sp<FrontendClient> openFrontend(int32_t frontendHandle); + sp<FrontendClient> openFrontend(int64_t frontendHandle); /** * Retrieve the granted frontend's information. @@ -81,7 +81,7 @@ public: * @param demuxHandle the handle of the demux granted by TRM. * @return a newly created DemuxClient interface. */ - sp<DemuxClient> openDemux(int32_t demuxHandle); + sp<DemuxClient> openDemux(int64_t demuxHandle); /** * Retrieve the DemuxInfo of a specific demux @@ -89,7 +89,7 @@ public: * @param demuxHandle the handle of the demux to query demux info for * @return the demux info */ - shared_ptr<DemuxInfo> getDemuxInfo(int32_t demuxHandle); + shared_ptr<DemuxInfo> getDemuxInfo(int64_t demuxHandle); /** * Retrieve a list of demux info @@ -111,7 +111,7 @@ public: * @param descramblerHandle the handle of the descrambler granted by TRM. * @return a newly created DescramblerClient interface. */ - sp<DescramblerClient> openDescrambler(int32_t descramblerHandle); + sp<DescramblerClient> openDescrambler(int64_t descramblerHandle); /** * Open a new interface of LnbClient given an lnbHandle. @@ -119,7 +119,7 @@ public: * @param lnbHandle the handle of the LNB granted by TRM. * @return a newly created LnbClient interface. */ - sp<LnbClient> openLnb(int32_t lnbHandle); + sp<LnbClient> openLnb(int64_t lnbHandle); /** * Open a new interface of LnbClient given a LNB name. diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml index 6a241b776aef..2a8f17b3d379 100644 --- a/packages/CompanionDeviceManager/res/values-af/strings.xml +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Laat <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toe om <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te bestuur?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"toestel"</string> <string name="summary_glasses" msgid="5469208629679579157">"Hierdie app sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> hê"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestemming om jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> se apps en stelselkenmerke na <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> te stroom?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sal toegang hê tot enigiets wat sigbaar is of gespeel word op jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, insluitend oudio, foto’s, betaalinligting, wagwoorde en boodskappe.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> sal apps na <xliff:g id="DEVICE_NAME">%3$s</xliff:g> kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en stelselkenmerke vanaf jou <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> te stroom"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang tot hierdie inligting op jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toegang tot jou <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> se foto’s, media en kennisgewings"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestemming om jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> se apps na <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong&gt te stroom?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sal toegang hê tot enigiets wat sigbaar is of gespeel word op <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, insluitend oudio, foto’s, wagwoorde en boodskappe.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> sal apps na <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> kan stroom totdat jy toegang tot hierdie toestemming verwyder."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps vanaf jou <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> te stroom"</string> <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string> <string name="summary_generic" msgid="1761976003668044801">"Hierdie app sal inligting kan sinkroniseer, soos die naam van iemand wat bel, tussen jou foon en die gekose toestel"</string> <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml index a9f5ed261a72..b66860eb5a42 100644 --- a/packages/CompanionDeviceManager/res/values-am/strings.xml +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ን እንዲያስተዳድር ይፈቅዳሉ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"መሣሪያ"</string> <string name="summary_glasses" msgid="5469208629679579157">"ይህ መተግበሪያ በእርስዎ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ላይ እነዚህን ፈቃዶች እንዲደርስ ይፈቀድለታል"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> መተግበሪያዎች እና የሥርዓት ባህሪያት ወደ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>? በዥረት እንዲለቅ ይፍቀዱ"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ኦዲዮ፣ ፎቶዎች፣ የክፍያ መረጃ፣ የይለፍ ቃላት እና መልዕክቶችን ጨምሮ በእርስዎ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ላይ የሚታየውን ወይም የሚጫወተውን የማንኛውም ነገር መዳረሻ ይኖረዋል።<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> የዚህን መዳረሻ እስኪያስወግዱ ድረስ መተግበሪያዎችን ወደ <xliff:g id="DEVICE_NAME">%3$s</xliff:g> በዥረት መልቀቅ ይችላል።"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ን በመወከል ከእርስዎ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>የመጡ መተግበሪያዎችን እና የሥርዓት ባህሪያትን በዥረት ለመልቀቅ ፈቃድ እየጠየቀ ነው"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ይህን መረጃ ከእርስዎ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> እንዲደርስ ይፍቀዱ"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ፎቶዎች፣ ሚዲያ እና ማሳወቂያዎች ለመድረስ የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> መተግበሪያዎች ወደ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>? በዥረት እንዲለቅ ይፍቀዱ"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ኦዲዮ፣ ፎቶዎች፣ የክፍያ መረጃ፣ የይለፍ ቃላት እና መልዕክቶችን ጨምሮ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ላይ የሚታየውን ወይም የሚጫወተውን የማንኛውም ነገር መዳረሻ ይኖረዋል።<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> የዚህን መዳረሻ እስኪያስወግዱ ድረስ መተግበሪያዎችን ወደ <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> በዥረት መልቀቅ ይችላል።"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> መተግበሪያዎችን ከእርስዎ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> በዥረት ለመልቀቅ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ን በመወከል ፈቃድ እየጠየቀ ነው"</string> <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string> <string name="summary_generic" msgid="1761976003668044801">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን በስልክዎ እና በተመረጠው መሣሪያ መካከል ማስመር ይችላል"</string> <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml index ce68ee1ef366..6f17676bec03 100644 --- a/packages/CompanionDeviceManager/res/values-ar/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"هل تريد السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بإدارة <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>؟"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"جهاز"</string> <string name="summary_glasses" msgid="5469208629679579157">"سيتم السماح لهذا التطبيق باستخدام هذه الأذونات على <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"هل تريد السماح لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ببث التطبيقات وميزات النظام من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> إلى <xliff:g id="DEVICE_NAME">%3$s</xliff:g>؟"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"سيتمكّن \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.<br/><br/>سيتمكّن \"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" من بثّ التطبيقات إلى <xliff:g id="DEVICE_NAME">%3$s</xliff:g> إلى أن توقِف إمكانية استخدام هذا الإذن."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_NAME">%2$s</xliff:g> لبثّ التطبيقات وميزات النظام من <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"هل تريد السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى هذه المعلومات من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>؟"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_NAME">%2$s</xliff:g> للوصول إلى الصور والوسائط والإشعارات في <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"هل تريد السماح لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" ببث التطبيقات من <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> إلى <xliff:g id="DEVICE_NAME">%3$s</xliff:g>؟"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"سيتمكّن \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" من الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله على <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>، بما في ذلك الملفات الصوتية والصور ومعلومات الدفع وكلمات المرور والرسائل.<br/><br/>سيتمكّن \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" من بثّ التطبيقات إلى <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> إلى أن توقِف إمكانية استخدام هذا الإذن."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_NAME">%2$s</xliff:g> لبثّ التطبيقات من <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string> <string name="summary_generic" msgid="1761976003668044801">"سيتمكّن هذا التطبيق من مزامنة المعلومات، مثل اسم المتصل، بين هاتفك والجهاز المحدّد."</string> <string name="consent_yes" msgid="8344487259618762872">"السماح"</string> diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml index 7376cd066d00..8b001d18643b 100644 --- a/packages/CompanionDeviceManager/res/values-as/strings.xml +++ b/packages/CompanionDeviceManager/res/values-as/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> পৰিচালনা কৰিবলৈ দিবনে?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ডিভাইচ"</string> <string name="summary_glasses" msgid="5469208629679579157">"এই এপ্টোক আপোনাৰ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ এপ্ আৰু ছিষ্টেমৰ সুবিধাসমূহ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ত ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ত দৃশ্যমান বা প্লে’ কৰা অডিঅ’, ফট’ পৰিশোধৰ তথ্য, পাছৱৰ্ড আৰু বাৰ্তাকে ধৰি যিকোনো বস্তু এক্সেছ কৰিব পাৰিব।<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g>এ আপুনি এই অনুমতিৰ এক্সেছ আঁতৰাই নিদিয়া পৰ্যন্ত <xliff:g id="DEVICE_NAME">%3$s</xliff:g>ত এপ্সমূহ ষ্ট্ৰীম কৰিব পাৰিব।"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ৰ পৰা এপ্ আৰু ছিষ্টেমৰ সুবিধাসমূহ ষ্ট্ৰীম কৰিবলৈ অনুমতি বিচাৰি অনুৰোধ জনাইছে"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ পৰা এই তথ্যখিনি এক্সেছ কৰিবলৈ দিয়ক"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ৰ ফট’, মিডিয়া আৰু জাননী এক্সেছ কৰাৰ বাবে অনুমতি বিচাৰি অনুৰোধ জনাইছে"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ এপ্সমূহ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ত ষ্ট্ৰীম কৰিবলৈ দিবনে?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>এ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>ত দৃশ্যমান হোৱা বা প্লে’ কৰা অডিঅ’, ফট’ পাছৱৰ্ড আৰু বাৰ্তাকে ধৰি যিকোনো বস্তু এক্সেছ কৰিব পাৰিব।<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g>এ আপুনি এই অনুমতিৰ এক্সেছ আঁতৰাই নিদিয়া পৰ্যন্ত <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>ত এপ্সমূহ ষ্ট্ৰীম কৰিব পাৰিব।"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ৰ পৰা এপ্সমূহ ষ্ট্ৰীম কৰিবলৈ অনুমতি বিচাৰি অনুৰোধ জনাইছে"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string> <string name="summary_generic" msgid="1761976003668044801">"এই এপ্টোৱে আপোনাৰ ফ’ন আৰু বাছনি কৰা ডিভাইচটোৰ মাজত কল কৰোঁতাৰ নামৰ দৰে তথ্য ছিংক কৰিব পাৰিব"</string> <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml index dd720935fdb9..b4fdcecd6d05 100644 --- a/packages/CompanionDeviceManager/res/values-az/strings.xml +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazını idarə etmək icazəsi verilsin?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"cihazda"</string> <string name="summary_glasses" msgid="5469208629679579157">"Bu tətbiq <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazında bu icazələrə daxil ola biləcək"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazının tətbiq və sistem funksiyalarını <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> cihazında yayımlamaq üçün <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə icazə verilsin?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> audio, foto, ödəniş məlumatı, parol və mesajlar daxil olmaqla <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazında görünən və ya oxudulan kontentə giriş əldə edəcək.<br/><br/>Siz bu icazəyə girişi silənə qədər <xliff:g id="APP_NAME_1">%1$s</xliff:g> tətbiqləri <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cihazında yayımlaya biləcək."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazından tətbiqləri və sistem fuksiyalarını yayımlamaq üçün <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından icazə tələb edir"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazından əldə edilən bu məlumata giriş icazəsi verin"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> üzrə foto, media və bildirişlərə daxil olmaq üçün <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından icazə tələb edir"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazının tətbiqlərini <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> cihazına yayımlamaq üçün <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə icazə verilsin?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> audio, foto, ödəniş məlumatı, parol və mesajlar daxil olmaqla <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> cihazında görünən və ya oxudulan kontentə giriş əldə edəcək.<br/><br/>Siz bu icazəyə girişi silənə qədər <xliff:g id="APP_NAME_2">%1$s</xliff:g> tətbiqləri <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cihazında yayımlaya biləcək."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> cihazından tətbiqləri yayımlamaq üçün <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından icazə tələb edir"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> <string name="summary_generic" msgid="1761976003668044801">"Tətbiq zəng edənin adı kimi məlumatları telefon ilə seçilmiş cihaz arasında sinxronlaşdıracaq"</string> <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml index 2200cecd0256..ef97da99afaa 100644 --- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Želite li da dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string> <string name="summary_glasses" msgid="5469208629679579157">"Ovoj aplikaciji će biti dozvoljeno da pristupa ovim dozvolama na uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Želite da dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> strimuje aplikacije i sistemske funkcije uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što se vidi ili pušta na uređaju <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, uključujući zvuk, slike, informacije o plaćanju, lozinke i poruke.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> će moći da strimuje aplikacije na <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup ovoj dozvoli."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da strimuje aplikacije i sistemske funkcije sa uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama sa uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da pristupa slikama, medijskom sadržaju i obaveštenjima sa uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Želite da dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> strimuje aplikacije uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što se vidi ili pušta na uređaju <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, uključujući zvuk, slike, informacije o plaćanju, lozinke i poruke.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> će moći da strimuje aplikacije na <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> dok ne uklonite pristup ovoj dozvoli."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> traži dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da strimuje aplikacije sa uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="summary_generic" msgid="1761976003668044801">"Ova aplikacija će moći da sinhronizuje podatke, poput imena osobe koja upućuje poziv, između telefona i odabranog uređaja"</string> <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index 7eca403fd5f0..c0aaac9c4e38 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Дазволіць праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> кіраваць прыладай <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"прылада"</string> <string name="summary_glasses" msgid="5469208629679579157">"Гэта праграма будзе мець на вашай прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>\" наступныя дазволы"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Дазволіць прыладзе <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> трансліраваць праграмы і сістэмныя функцыі прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на прыладу <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будзе мець доступ да ўсяго, што паказваецца ці прайграецца на прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\", у тым ліку да аўдыя, фота, плацежнай інфармацыі, пароляў і паведамленняў.<br/><br/>Праграма \"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" зможа трансліраваць праграмы на прыладу \"<xliff:g id="DEVICE_NAME">%3$s</xliff:g>\", пакуль вы не адклічаце гэты дазвол."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на трансляцыю праграмы і сістэмных функцый з прылады тыпу \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\""</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на доступ да фота, медыяфайлаў і апавяшчэнняў на прыладзе тыпу \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Дазволіць праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> трансліраваць праграмы прылады тыпу \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на прыладу <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будзе мець доступ да ўсяго, што паказваецца ці прайграецца на прыладзе \"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>\", у тым ліку да аўдыя, фота, плацежнай інфармацыі, пароляў і паведамленняў.<br/><br/>Праграма \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" зможа трансліраваць праграмы на прыладу \"<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>\", пакуль вы не адклічаце гэты дазвол."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на трансляцыю праграм з прылады тыпу \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string> <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string> <string name="summary_generic" msgid="1761976003668044801">"Гэта праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) паміж тэлефонам і выбранай прыладай"</string> <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index 3ebf375cdbee..0fa98ef836ab 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Разрешавате ли на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управлява устройството <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"устройство"</string> <string name="summary_glasses" msgid="5469208629679579157">"Това приложение ще има достъп до следните разрешения за вашите <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Да се разреши ли на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да предава поточно към <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> приложенията и системните функции на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ще има достъп до всичко, което се показва или възпроизвежда на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, включително аудио, снимки, данни за плащане, пароли и съобщения.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> ще може да предава поточно приложения към устройството ви <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, докато не премахнете това разрешение."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да предава поточно приложения и системни функции от устройството ви от тип <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от вашия <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на ваше устройство (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) за достъп до снимките, мултимедията и известията на вашия <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Да се разреши ли на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да предава поточно към <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> приложенията на устройството ви от тип <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ще има достъп до всичко, което се показва или възпроизвежда на устройството ви <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, включително аудио, снимки, пароли и съобщения.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> ще може да предава поточно приложения към устройството ви <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, докато не премахнете това разрешение."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да предава поточно приложения от устройството ви от тип <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> <string name="summary_generic" msgid="1761976003668044801">"Това приложение ще може да синхронизира различна информация, като например името на обаждащия се, между телефона ви и избраното устройство"</string> <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml index 84316f299808..183bdc8a86c8 100644 --- a/packages/CompanionDeviceManager/res/values-bs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aplikaciji će biti dozvoljen pristup ovim odobrenjima koje sadržava vaš <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da prenosi aplikacije i funkcije sistema koje sadržava vaš <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaju <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> reproducira ili je vidljivo na njemu, uključujući zvukove, fotografije, podatke o plaćanju, lozinke i poruke.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> će moći prenositi aplikacije na uređaju <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup ovom odobrenju."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da prenosi aplikacije i funkcije sistema s uređaja vrste \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Dozvolite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa ovim informacijama koje sadržava vaš <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime vašeg uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da pristupa fotografijama, medijima i obavještenjima koje sadržava vaš <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da prenosi aplikacije koje sadržava vaš <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaju <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> će imati pristup svemu što <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> reproducira ili je vidljivo na njemu, uključujući zvukove, fotografije, podatke o plaćanju, lozinke i poruke.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> će moći prenositi aplikacije na uređaju <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> dok ne uklonite pristup ovom odobrenju."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> traži odobrenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da prenosi aplikacije s uređaja vrste \"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\""</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="summary_generic" msgid="1761976003668044801">"Ova aplikacija će moći sinhronizirati informacije, kao što je ime osobe koja upućuje poziv, između vašeg telefona i odabranog uređaja"</string> <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index 8b115f74b3b5..9b321a8e305d 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositiu"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aquesta aplicació podrà accedir a aquests permisos del <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Vols permetre que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> reprodueixi en continu les aplicacions i les funcions del sistema del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a <xliff:g id="DEVICE_NAME">%3$s</xliff:g>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> podrà reproduir en continu aplicacions al dispositiu <xliff:g id="DEVICE_NAME">%3$s</xliff:g> fins que suprimeixis l\'accés a aquest permís."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per reproduir en continu aplicacions i funcions del sistema del teu dispositiu (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a aquesta informació del <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per accedir a les fotos, el contingut multimèdia i les notificacions del <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vols permetre que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> reprodueixi en continu les aplicacions del dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) a <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g><strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> podrà accedir a qualsevol cosa que sigui visible o que es reprodueixi al dispositiu <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclosos àudios, fotos, informació de pagament, contrasenyes i missatges.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> podrà reproduir en continu aplicacions al dispositiu <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> fins que suprimeixis l\'accés a aquest permís."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del dispositiu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> per reproduir en continu aplicacions del teu dispositiu (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string> <string name="summary_generic" msgid="1761976003668044801">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, entre el teu telèfon i el dispositiu triat"</string> <string name="consent_yes" msgid="8344487259618762872">"Permet"</string> diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml index 7bd6e38a919b..b08081b4840d 100644 --- a/packages/CompanionDeviceManager/res/values-cs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> spravovat zařízení <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"zařízení"</string> <string name="summary_glasses" msgid="5469208629679579157">"Tato aplikace bude mít na zařízení typu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> přístup k následujícím oprávněním"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovat aplikace a systémové funkce ze zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zařízení <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mít přístup ke všemu, co na zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zobrazíte nebo přehrajete, včetně zvuku, fotek, platebních údajů, hesel a zpráv.<br/><br/>Aplikace <xliff:g id="APP_NAME_1">%1$s</xliff:g> bude moct streamovat aplikace do zařízení typu <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, dokud přístup k tomuto oprávnění neodeberete."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace a systémové funkce z vašeho zařízení typu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Povolte aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k těmto informacím z vašeho zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> oprávnění k přístupu k fotkám, médiím a oznámením na zařízení typu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovat aplikace na zařízení typu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zařízení <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mít přístup ke všemu, co na zařízení typu <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> zobrazíte nebo přehrajete, včetně zvuku, fotek, platebních údajů, hesel a zpráv.<br/><br/>Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> bude moct streamovat aplikace do zařízení typu <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, dokud přístup k tomuto oprávnění neodeberete."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace z vašeho zařízení typu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string> <string name="summary_generic" msgid="1761976003668044801">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, mezi vaším telefonem a vybraným zařízením"</string> <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml index 4180ef5baa7d..da3b26166692 100644 --- a/packages/CompanionDeviceManager/res/values-da/strings.xml +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du tillade, at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administrerer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"enhed"</string> <string name="summary_glasses" msgid="5469208629679579157">"Denne app får adgang til disse tilladelser på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Vil du give <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilladelse til at streame apps og systemfunktioner fra din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får adgang til alt, der er synligt eller afspilles på din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, herunder lyd, billeder, betalingsoplysninger, adgangskoder og beskeder.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> kan streame apps til <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, indtil du fjerner adgangen til denne tilladelse."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at streame apps og systemfunktioner fra din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Giv <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adgang til disse oplysninger fra din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at få adgang til billeder, medier og notifikationer på din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vil du give <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilladelse til at streame apps fra din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> får adgang til alt, der er synligt eller afspilles på <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, herunder lyd, billeder, betalingsoplysninger, adgangskoder og beskeder.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> kan streame apps til <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, indtil du fjerner adgangen til denne tilladelse."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at streame apps fra din <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string> <string name="summary_generic" msgid="1761976003668044801">"Denne app vil kunne synkronisere oplysninger som f.eks. navnet på en person, der ringer, mellem din telefon og den valgte enhed"</string> <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index 57aebc3a90aa..e465a3876308 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Να επιτρέπεται στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> να διαχειρίζεται τη συσκευή <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ;"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"συσκευή"</string> <string name="summary_glasses" msgid="5469208629679579157">"Αυτή η εφαρμογή θα μπορεί να έχει πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Να επιτρέπεται στο <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> η δυνατότητα ροής εφαρμογών και λειτουργιών συστήματος του <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> στο <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>;"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Το <xliff:g id="APP_NAME_0">%1$s</xliff:g> θα έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στο <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> σας, συμπεριλαμβανομένων ήχων, φωτογραφιών, στοιχείων πληρωμής, κωδικών πρόσβασης και μηνυμάτων.<br/><br/>Το <xliff:g id="APP_NAME_1">%1$s</xliff:g> θα έχει τη δυνατότητα ροής εφαρμογών στο <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, μέχρι να καταργήσετε την πρόσβαση σε αυτή την άδεια."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Το <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους του <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για τη ροή εφαρμογών και λειτουργιών συστήματος από το <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> σας"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Να επιτρέπεται στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> η πρόσβαση σε αυτές τις πληροφορίες από τη συσκευή σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>."</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους της συσκευής σας <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις της συσκευής <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Να επιτρέπεται στο <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> η δυνατότητα ροής εφαρμογών του <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> σας στο <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>;"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Το <xliff:g id="APP_NAME_0">%1$s</xliff:g> θα έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στο <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, συμπεριλαμβανομένων ήχων, φωτογραφιών, στοιχείων πληρωμής, κωδικών πρόσβασης και μηνυμάτων.<br/><br/>Το <xliff:g id="APP_NAME_2">%1$s</xliff:g> θα έχει τη δυνατότητα ροής εφαρμογών στο <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, μέχρι να καταργήσετε την πρόσβαση σε αυτή την άδεια."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Το <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους του <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για τη ροή εφαρμογών από το <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> σας"</string> <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string> <string name="summary_generic" msgid="1761976003668044801">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες μεταξύ του τηλεφώνου και της επιλεγμένης συσκευής σας, όπως το όνομα ενός ατόμου που σας καλεί."</string> <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml index 0a8542819ed5..92f0a1b3b93d 100644 --- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> <string name="summary_glasses" msgid="5469208629679579157">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps and system features to <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, payment info, passwords and messages.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to access your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\'s photos, media and notifications"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps to <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, including audio, photos, payment info, passwords and messages.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> until you remove access to this permission."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml index 0a8542819ed5..92f0a1b3b93d 100644 --- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> <string name="summary_glasses" msgid="5469208629679579157">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps and system features to <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, payment info, passwords and messages.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to access your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\'s photos, media and notifications"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps to <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, including audio, photos, payment info, passwords and messages.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> until you remove access to this permission."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml index 0a8542819ed5..92f0a1b3b93d 100644 --- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> <string name="summary_glasses" msgid="5469208629679579157">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps and system features to <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, including audio, photos, payment info, passwords and messages.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME">%3$s</xliff:g> until you remove access to this permission."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and system features from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to access your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>\'s photos, media and notifications"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to stream your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\'s apps to <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> will have access to anything that\'s visible or played on <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, including audio, photos, payment info, passwords and messages.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> will be able to stream apps to <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> until you remove access to this permission."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps from your <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml index a1cb0c6e857d..8099537f41ac 100644 --- a/packages/CompanionDeviceManager/res/values-et/strings.xml +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hallata seadet <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"seade"</string> <string name="summary_glasses" msgid="5469208629679579157">"Sellel rakendusel lubatakse juurde pääseda nendele lubadele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Kas lubada rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> teie seadme <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> rakendusi ja süsteemifunktsioone seadmesse <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> voogesitada?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saab juurdepääsu kõigele, mida teie seadmes <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> saab kuvada või esitada, sh helile, fotodele, makseteabele, paroolidele ja sõnumitele.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> saab rakendusi seadmesse <xliff:g id="DEVICE_NAME">%3$s</xliff:g> voogesitada seni, kuni juurdepääsu sellele loale eemaldate."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba rakenduste ja süsteemifunktsioonide voogesitamiseks teie seadmest <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pääseda juurde sellele teabele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba pääseda juurde fotodele, meediale ja märguannetele, mille asukoht on teie <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Kas lubada rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> teie seadme <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> rakendusi seadmesse <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> voogesitada?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saab juurdepääsu kõigele, mida teie seadmes <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> saab kuvada või esitada, sh helile, fotodele, makseteabele, paroolidele ja sõnumitele.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> saab rakendusi seadmesse <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> voogesitada seni, kuni juurdepääsu sellele loale eemaldate."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba rakenduste voogesitamiseks teie seadmest <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string> <string name="summary_generic" msgid="1761976003668044801">"See rakendus saab sünkroonida teavet, näiteks helistaja nime, teie telefoni ja valitud seadme vahel"</string> <string name="consent_yes" msgid="8344487259618762872">"Luba"</string> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index f88af3ce76e5..dd9b47c21f79 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> kudeatzeko baimena eman nahi diozu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"gailua"</string> <string name="summary_glasses" msgid="5469208629679579157">"Baimen hauek izango ditu aplikazioak <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> erabiltzean:"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari zure <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuko aplikazioak eta sistemaren eginbideak <xliff:g id="DEVICE_NAME">%3$s</xliff:g> gailura zuzenean igortzeko baimena eman nahi diozu?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, ordainketa-informazioa, pasahitzak eta mezuak barne.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g> gailura aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> gailutik aplikazioak eta sistemaren eginbideak zuzenean igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Eman informazioa <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailutik hartzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> gailuko argazkiak, multimedia-edukia eta jakinarazpenak atzitzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari zure <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuko aplikazioak <xliff:g id="DEVICE_NAME">%3$s</xliff:g> gailura zuzenean igortzeko baimena eman nahi diozu?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> gailuan ikusgai dagoen edo erreproduzitzen den eduki guztia atzitu ahal izango du, audioa, argazkiak, ordainketa-informazioa, pasahitzak eta mezuak barne.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> gailura aplikazioak zuzenean igortzeko gai izango da, baimen hori kentzen diozun arte."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> gailutik aplikazioak zuzenean igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string> <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string> <string name="summary_generic" msgid="1761976003668044801">"Telefonoaren eta hautatutako gailuaren artean informazioa sinkronizatzeko gai izango da aplikazioa (esate baterako, deitzaileen izenak)"</string> <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index 9066c6a1cb7c..7b013bff0016 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه داده شود <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> را مدیریت کند؟"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"دستگاه"</string> <string name="summary_glasses" msgid="5469208629679579157">"این برنامه قادر خواهد بود به این اجازهها در <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> شما دسترسی پیدا کند"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه میدهید برنامهها و ویژگیهای سیستم <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> را در <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> جاریسازی کند؟"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> به هرچیزی که در <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> شما نمایان است یا پخش میشود، ازجمله صداها، عکسها، اطلاعات پرداخت، گذرواژهها، و پیامها دسترسی خواهد داشت.<br/><br/>تا زمانیکه دسترسی به این اجازه را حذف نکنید، <xliff:g id="APP_NAME_1">%1$s</xliff:g> میتواند برنامهها را در <xliff:g id="DEVICE_NAME">%3$s</xliff:g> جاریسازی کند."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه میخواهد برنامهها و ویژگیهای سیستم را از <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> شما جاریسازی کند"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"اجازه دادن به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای دسترسی به این اطلاعات در <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> شما"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه میخواهد به عکسها، رسانهها، و اعلانهای <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> شما دسترسی پیدا کند"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه میدهید برنامههای <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> را در <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> جاریسازی کند؟"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> به هرچیزی که در <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> شما نمایان است یا پخش میشود، ازجمله صداها، عکسها، اطلاعات پرداخت، گذرواژهها، و پیامها دسترسی خواهد داشت.<br/><br/>تا زمانیکه دسترسی به این اجازه را حذف نکنید، <xliff:g id="APP_NAME_2">%1$s</xliff:g> میتواند برنامهها را در <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> جاریسازی کند."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه میخواهد برنامهها را از <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> شما جاریسازی کند"</string> <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string> <string name="summary_generic" msgid="1761976003668044801">"این برنامه مجاز میشود اطلاعتی مثل نام شخصی را که تماس میگیرد بین تلفن شما و دستگاه انتخابشده همگامسازی کند"</string> <string name="consent_yes" msgid="8344487259618762872">"اجازه دادن"</string> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml index e155077142b0..f20b71b63c47 100644 --- a/packages/CompanionDeviceManager/res/values-fi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Salli, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saa ylläpitää laitetta: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"laite"</string> <string name="summary_glasses" msgid="5469208629679579157">"Sovellus saa käyttää näitä lupia <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Saako <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> striimata <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> olevia sovelluksia ja järjestelmäominaisuuksia laitteelle (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saa pääsyn kaikkeen <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> näkyvään tai pelattavaan sisältöön, mukaan lukien audioon, kuviin, maksutietoihin, salasanoihin ja viesteihin.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> voi striimata sovelluksia laitteelle (<xliff:g id="DEVICE_NAME">%3$s</xliff:g>), kunnes poistat luvan."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteelta (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) lupaa striimata sovelluksia ja järjestelmän ominaisuuksia laitteeltasi (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Salli, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saa pääsyn näihin <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> oleviin tietoihin"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa päästä <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> oleviin kuviin, mediaan ja ilmoituksiin"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Saako <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> striimata <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> olevia sovelluksia laitteelle (<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>)?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> saa pääsyn kaikkeen <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> näkyvään tai pelattavaan sisältöön, mukaan lukien audioon, kuviin, salasanoihin ja viesteihin.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> voi striimata sovelluksia laitteelle (<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>), kunnes poistat luvan."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää lapsen (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia laitteeltasi (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)"</string> <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string> <string name="summary_generic" msgid="1761976003668044801">"Sovellus voi synkronoida tietoja (esimerkiksi soittajan nimen) puhelimesi ja valitun laitteen välillä"</string> <string name="consent_yes" msgid="8344487259618762872">"Salli"</string> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index 85bfdc0f33a5..a2bd0f8e449f 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Queres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> xestione o dispositivo (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>)?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"Esta aplicación poderá acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Queres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> emita as aplicacións e as funcións do sistema do dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) en <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acceso a todo o que se vexa ou reproduza no teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), como audio, fotos, información de pago, contrasinais e mensaxes.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá emitir aplicacións en <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ata que quites o acceso a este permiso."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome dun dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para emitir aplicacións e funcións do sistema do seguinte aparello: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información do dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para acceder ás fotos, ao contido multimedia e ás notificacións do seguinte aparello: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Queres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> emita as aplicacións do dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) en <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acceso a todo o que se vexa ou reproduza no teu dispositivo (<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>), como audio, fotos, información de pago, contrasinais e mensaxes.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> poderá emitir aplicacións en <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> ata que quites o acceso a este permiso."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome dun dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para emitir aplicacións do seguinte aparello: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) entre o teléfono e o dispositivo escollido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml index 9effe2ce2226..c18ebc0b6559 100644 --- a/packages/CompanionDeviceManager/res/values-gu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> મેનેજ કરવા માટે મંજૂરી આપીએ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ડિવાઇસ"</string> <string name="summary_glasses" msgid="5469208629679579157">"આ ઍપને તમારા <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી મળશે"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"શું <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ની ઍપ અને સિસ્ટમની સુવિધાઓને <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> પર સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>ની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, પાસવર્ડ અને મેસેજ શામેલ છે.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> ત્યાં સુધી ઍપ અને સિસ્ટમની સુવિધાઓને <xliff:g id="DEVICE_NAME">%3$s</xliff:g> પર સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>માંથી ઍપ અને સિસ્ટમની સુવિધાઓ સ્ટ્રીમ કરવા માટે <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"તમારા <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>માંથી આ માહિતી ઍક્સેસ કરવા માટે, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને મંજૂરી આપો"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ના ફોટા, મીડિયા અને નોટિફિકેશન ઍક્સેસ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"શું <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ની ઍપને <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> પર સ્ટ્રીમ કરવાની મંજૂરી આપીએ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>ની પાસે એવી બધી બાબતોનો ઍક્સેસ રહેશે જે <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> પર જોઈ શકાતી કે ચલાવી શકાતી હોય, જેમાં ઑડિયો, ફોટા, ચુકવણીની માહિતી, પાસવર્ડ અને મેસેજ શામેલ છે.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> ત્યાં સુધી ઍપને <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> પર સ્ટ્રીમ કરી શકશે, જ્યાં સુધી તમે આ પરવાનગીનો ઍક્સેસ કાઢી નહીં નાખો."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>માંથી ઍપ સ્ટ્રીમ કરવા માટે <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string> <string name="summary_generic" msgid="1761976003668044801">"આ ઍપ તમારા ફોન અને પસંદ કરેલા ડિવાઇસ વચ્ચે, કૉલ કરનાર કોઈ વ્યક્તિનું નામ જેવી માહિતી સિંક કરી શકશે"</string> <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index 2a08e0030f8f..562f7625ce8c 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"क्या <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> मैनेज करने की अनुमति देनी है?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"डिवाइस"</string> <string name="summary_glasses" msgid="5469208629679579157">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"क्या <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> में मौजूद ऐप्लिकेशन और सिस्टम की सुविधाओं को <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> पर स्ट्रीम करने की अनुमति देनी है?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> के पास ऐसे किसी भी कॉन्टेंट का ऐक्सेस होगा जो आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> पर दिखता है या चलाया जाता है. इसमें ऑडियो, फ़ोटो, पेमेंट संबंधी जानकारी, पासवर्ड, और मैसेज शामिल हैं.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%3$s</xliff:g> पर तब ऐप्लिकेशन को स्ट्रीम कर सकेगा, जब तक आप यह अनुमति हटा न दें."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> को <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, आपके <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> में मौजूद ऐप्लिकेशन और सिस्टम की सुविधाओं को स्ट्रीम करने की अनुमति चाहिए"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की यह जानकारी ऐक्सेस करने दें"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"आपके <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से <xliff:g id="APP_NAME">%1$s</xliff:g>, आपके <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रहा है"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"क्या <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> में मौजूद ऐप्लिकेशन को <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> पर स्ट्रीम करने की अनुमति देनी है?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> के पास ऐसे किसी भी कॉन्टेंट का ऐक्सेस होगा जो आपके <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> पर दिखता है या चलाया जाता है. इसमें ऑडियो, फ़ोटो, पेमेंट संबंधी जानकारी, पासवर्ड, और मैसेज शामिल हैं.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> पर तब ऐप्लिकेशन को स्ट्रीम कर सकेगा, जब तक आप यह अनुमति हटा न दें."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> को <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, आपके <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> में मौजूद ऐप्लिकेशन को स्ट्रीम करने की अनुमति चाहिए"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string> <string name="summary_generic" msgid="1761976003668044801">"यह ऐप्लिकेशन, आपके फ़ोन और चुने हुए डिवाइस के बीच जानकारी सिंक करेगा. जैसे, कॉल करने वाले व्यक्ति का नाम"</string> <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index 6b3e20419be5..17b45389848c 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aplikacija će moći pristupati ovim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Želite li dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da streama aplikacije i značajke sustava uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaj <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> imat će pristup svemu što je vidljivo ili se reproducira na uređaju <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, uključujući zvuk, fotografije, podatke o plaćanju, zaporke i poruke.<br/><br/>Aplikacija <xliff:g id="APP_NAME_1">%1$s</xliff:g> moći će streamati aplikacije na uređaj <xliff:g id="DEVICE_NAME">%3$s</xliff:g> dok ne uklonite pristup za to dopuštenje."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za streaming aplikacija i značajki sustava s vašeg uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Omogućite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa informacijama s vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za pristup fotografijama, medijskim sadržajima i obavijestima na uređaju <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Želite li dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da streama aplikacije uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na uređaj <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> imat će pristup svemu što je vidljivo ili se reproducira na uređaju <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, uključujući zvuk, fotografije, podatke o plaćanju, zaporke i poruke.<br/><br/>Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> moći će streamati aplikacije na uređaj <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> dok ne uklonite pristup za to dopuštenje."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za streaming aplikacija s vašeg uređaja <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="summary_generic" msgid="1761976003668044801">"Ta će aplikacija moći sinkronizirati podatke između vašeg telefona i odabranog uređaja, primjerice ime pozivatelja"</string> <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml index 31d98283fed6..4b0dd491543d 100644 --- a/packages/CompanionDeviceManager/res/values-hu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Engedélyezi, hogy a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> kezelje a következő eszközt: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"eszköz"</string> <string name="summary_glasses" msgid="5469208629679579157">"Az alkalmazás hozzáférhet majd ezekhez az engedélyekhez a következőn: <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Engedélyezi a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> számára a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> alkalmazásainak és rendszerfunkcióinak streamelését a következőre: <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> hozzáférhet a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> minden látható vagy lejátszható tartalmához, így az audiotartalmakhoz, fényképekhez, fizetési adatokhoz, jelszavakhoz és üzenetekhez is.<br/><br/>Amíg Ön el nem távolítja az ehhez az engedélyhez való hozzáférést, a(z) <xliff:g id="APP_NAME_1">%1$s</xliff:g> képes lesz majd az alkalmazások <xliff:g id="DEVICE_NAME">%3$s</xliff:g> eszközre való streamelésére."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében az alkalmazások és rendszerfunkciók következőről való streameléséhez: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Engedélyezi a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazás számára az ehhez az információhoz való hozzáférést a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> esetén"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében a(z) <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> fotóihoz, médiatartalmaihoz és értesítéseihez való hozzáféréshez"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Engedélyezi a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> számára a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> alkalmazásainak streamelését a következőre: <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> hozzáférhet a(z) <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> minden látható vagy lejátszható tartalmához, így az audiotartalmakhoz, fényképekhez, fizetési adatokhoz, jelszavakhoz és üzenetekhez is.<br/><br/>Amíg Ön el nem távolítja az ehhez az engedélyhez való hozzáférést, a(z) <xliff:g id="APP_NAME_2">%1$s</xliff:g> képes lesz majd az alkalmazások <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> eszközre való streamelésére."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében az alkalmazások következőről való streameléséhez: <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string> <string name="summary_generic" msgid="1761976003668044801">"Ez az alkalmazás képes lesz szinkronizálni az olyan információkat a telefon és a kiválasztott eszköz között, mint például a hívó fél neve."</string> <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index 2ee4e89a448d..abb02992fc40 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengelola <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"perangkat"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aplikasi ini akan diizinkan mengakses izin ini di <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> Anda"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> melakukan streaming aplikasi dan fitur sistem <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ke <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan memiliki akses ke apa pun yang ditampilkan atau diputar di <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, termasuk audio, foto, info pembayaran, sandi, dan pesan.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> akan dapat melakukan streaming aplikasi ke <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hingga Anda menghapus izin ini."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk melakukan streaming aplikasi dan fitur sistem dari <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> Anda"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengakses informasi ini dari <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> Anda"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk mengakses foto, media, dan notifikasi <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> Anda"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> melakukan streaming aplikasi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ke <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> akan memiliki akses ke apa pun yang ditampilkan atau diputar di <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, termasuk audio, foto, info pembayaran, sandi, dan pesan.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> akan dapat melakukan streaming aplikasi ke <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hingga Anda menghapus izin ini."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk melakukan streaming aplikasi dari <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> Anda"</string> <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string> <string name="summary_generic" msgid="1761976003668044801">"Aplikasi ini akan dapat menyinkronkan info, seperti nama penelepon, antara ponsel dan perangkat yang dipilih"</string> <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index d86f5da3f3fa..7294e160a6ee 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Leyfa <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> að stjórna <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"tæki"</string> <string name="summary_glasses" msgid="5469208629679579157">"Þetta forrit fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Leyfa <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> að streyma forritum og kerfiseiginleikum <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> í <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> fær aðgang að öllu sem er sýnilegt eða spilað í <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, þ.m.t. hljóði, myndum, greiðsluupplýsingum, aðgangsorðum og skilaboðum.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> getur streymt forritum í <xliff:g id="DEVICE_NAME">%3$s</xliff:g> þar til þú fjarlægir þessa heimild."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir hönd <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum og kerfiseiginleikum úr <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DEVICE_NAME">%2$s</xliff:g> vegna aðgangs að myndum, margmiðlunarefni og tilkynningum í <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Leyfa <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> að streyma forritum <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> í <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> fær aðgang að öllu sem er sýnilegt eða spilað í <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, þ.m.t. hljóði, myndum, greiðsluupplýsingum, aðgangsorðum og skilaboðum.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> getur streymt forritum í <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> þar til þú fjarlægir þessa heimild."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir hönd <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum úr <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string> <string name="summary_generic" msgid="1761976003668044801">"Þetta forrit mun geta samstillt upplýsingar, t.d. nafn þess sem hringir, á milli símans og valins tækis"</string> <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string> diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml index 2efb77e31905..16000312e31f 100644 --- a/packages/CompanionDeviceManager/res/values-iw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"מתן הרשאה לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong&g; לנהל את <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"מכשיר"</string> <string name="summary_glasses" msgid="5469208629679579157">"האפליקציה הזו תוכל לגשת להרשאות האלה ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> שלך"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"לאשר לאפליקציית <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לשדר את האפליקציות ותכונות המערכת של ה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> אל <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> תהיה גישה לכל מה שרואים או מפעילים ב<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, כולל אודיו, תמונות, פרטי תשלום, סיסמאות והודעות.<br/><br/>לאפליקציה <xliff:g id="APP_NAME_1">%1$s</xliff:g> תהיה אפשרות לשדר אפליקציות ל-<xliff:g id="DEVICE_NAME">%3$s</xliff:g> עד שהגישה להרשאה הזו תוסר."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לשדר אפליקציות ותכונות מערכת מה<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"מתן אישור לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לגשת למידע הזה מה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> שלך"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות ב<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"לאשר לאפליקציית <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לשדר את האפליקציות של ה<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ל-<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> תהיה גישה לכל מה שרואים או מפעילים ב-<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, כולל אודיו, תמונות, פרטי תשלום, סיסמאות והודעות.<br/><br/>לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> תהיה אפשרות לשדר אפליקציות ל-<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> עד שהגישה להרשאה הזו תוסר."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה ל-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי לשדר אפליקציות מה<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string> <string name="summary_generic" msgid="1761976003668044801">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, מהטלפון שלך למכשיר שבחרת"</string> <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml index f392c1050006..835154263673 100644 --- a/packages/CompanionDeviceManager/res/values-kk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> құрылғысын басқаруға рұқсат беру керек пе?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"құрылғы"</string> <string name="summary_glasses" msgid="5469208629679579157">"Бұл қолданба құрылғыда (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>) осы рұқсаттарды пайдалана алады."</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) қолданбалар мен жүйе функцияларын <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> құрылғысына трансляциялауға рұқсат берілсін бе?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасы құрылғыңызда (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) көрінетін не ойнатылатын барлық контентті, соның ішінде аудиофайлдарды, фотосуреттерді, төлем туралы ақпаратты, құпия сөздер мен хабарларды пайдалана алады.<br/><br/>Осы рұқсатты өшірмесеңіз, <xliff:g id="APP_NAME_1">%1$s</xliff:g> қолданбасы құрылғыға (<xliff:g id="DEVICE_NAME">%3$s</xliff:g>) қолданбаларды трансляциялай алады."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан құрылғыдағы (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) қолданбалар мен жүйе функцияларын трансляциялауға рұқсат сұрайды."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) осы ақпаратты пайдалануға рұқсат беріңіз."</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) фотосуреттерді, медиафайлдар мен хабарландыруларды пайдалануға рұқсат сұрайды."</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына құрылғыңыздағы (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) қолданбаларды <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> құрылғысына трансляциялауға рұқсат берілсін бе?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасы құрылғыда (<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>) көрінетін не ойнатылатын барлық контентті, соның ішінде аудиофайлдарды, фотосуреттерді, төлем туралы ақпаратты, құпия сөздер мен хабарларды пайдалана алады.<br/><br/>Осы рұқсатты өшірмесеңіз, <xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасы құрылғыға (<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>) қолданбаларды трансляциялай алады."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан құрылғыдағы (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>) қолданбаларды трансляциялауға рұқсат сұрайды."</string> <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string> <string name="summary_generic" msgid="1761976003668044801">"Бұл қолданба телефон мен таңдалған құрылғы арасында деректі (мысалы, қоңырау шалушының атын) синхрондай алады."</string> <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml index 149c62440309..79ec5f18437d 100644 --- a/packages/CompanionDeviceManager/res/values-km/strings.xml +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> គ្រប់គ្រង <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ឬ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ឧបករណ៍"</string> <string name="summary_glasses" msgid="5469208629679579157">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យចូលប្រើការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> របស់អ្នក"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធលើ<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>របស់អ្នកទៅ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ឬ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> នឹងមានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញ ឬត្រូវបានចាក់នៅលើ<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>របស់អ្នក រួមទាំងសំឡេង រូបថត ព័ត៌មាននៃការទូទាត់ប្រាក់ ពាក្យសម្ងាត់ និងសារ។<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> នឹងអាចផ្សាយកម្មវិធីទៅ <xliff:g id="DEVICE_NAME">%3$s</xliff:g> រហូតទាល់តែអ្នកដកសិទ្ធិចូលប្រើការអនុញ្ញាតនេះចេញ។"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ដើម្បីផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធពី<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>របស់អ្នក"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ចូលប្រើព័ត៌មាននេះពី <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នក"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីចូលប្រើរូបថត មេឌៀ និងការជូនដំណឹងរបស់ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> អ្នក"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ផ្សាយកម្មវិធីលើ<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>របស់អ្នកទៅ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ឬ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> នឹងមានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញ ឬត្រូវបានចាក់នៅលើ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> រួមទាំងសំឡេង រូបថត ព័ត៌មាននៃការទូទាត់ប្រាក់ ពាក្យសម្ងាត់ និងសារ។<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> នឹងអាចផ្សាយកម្មវិធីទៅ <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> រហូតទាល់តែអ្នកដកសិទ្ធិចូលប្រើការអនុញ្ញាតនេះចេញ។"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ដើម្បីផ្សាយកម្មវិធីពី<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>របស់អ្នក"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string> <string name="summary_generic" msgid="1761976003668044801">"កម្មវិធីនេះនឹងអាចធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម រវាងឧបករណ៍ដែលបានជ្រើសរើស និងទូរសព្ទរបស់អ្នក"</string> <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string> diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml index df7f4f461219..7cf406992472 100644 --- a/packages/CompanionDeviceManager/res/values-kn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? ನಿರ್ವಹಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ಸಾಧನ"</string> <string name="summary_glasses" msgid="5469208629679579157">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್ಗಳನ್ನು <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ಗೆ ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾವತಿ ಮಾಹಿತಿ, ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲದಕ್ಕೂ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ನೀವು ಈ ಅನುಮತಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ಗೆ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME_1">%1$s</xliff:g> ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ನಿಂದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಸಿಸ್ಟಮ್ ಫೀಚರ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಗಾಗಿ ವಿನಂತಿಸುತ್ತಿದೆ"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನಿಂದ ಈ ಮಾಹಿತಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಿ"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ನ ಫೋಟೋಗಳು, ಮೀಡಿಯಾ ಮತ್ತು ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಆ್ಯಪ್ಗಳನ್ನು <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ಗೆ ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ಆಡಿಯೋ, ಫೋಟೋಗಳು, ಪಾವತಿ ಮಾಹಿತಿ, ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಸಂದೇಶಗಳು ಸೇರಿದಂತೆ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ನಲ್ಲಿ ಗೋಚರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಆಗುವ ಎಲ್ಲದಕ್ಕೂ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ನೀವು ಈ ಅನುಮತಿಗೆ ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕುವವರೆಗೆ <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> ಗೆ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="APP_NAME_2">%1$s</xliff:g> ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ನಿಂದ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಗಾಗಿ ವಿನಂತಿಸುತ್ತಿದೆ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string> <string name="summary_generic" msgid="1761976003668044801">"ಮೊಬೈಲ್ ಫೋನ್ ಮತ್ತು ಆಯ್ಕೆಮಾಡಿದ ಸಾಧನದ ನಡುವೆ, ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string> <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 0192eea3d644..c3843633a07b 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> 기기를 관리하도록 허용하시겠습니까?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"기기"</string> <string name="summary_glasses" msgid="5469208629679579157">"앱이 <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>의 앱 및 시스템 기능을 <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> 기기로 스트리밍하도록 허용하시겠습니까?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>에서 오디오, 사진, 결제 정보, 비밀번호, 메시지 등 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>에 표시되거나 해당 기기에서 재생되는 모든 항목에 액세스할 수 있습니다.<br/><br/>이 권한에 대한 액세스를 삭제할 때까지 <xliff:g id="APP_NAME_1">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%3$s</xliff:g> 기기로 앱을 스트리밍할 수 있습니다."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>의 앱 및 시스템 기능을 스트리밍할 권한을 요청하고 있습니다."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>에서 이 정보에 액세스하도록 허용합니다."</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>의 앱을 <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> 기기로 스트리밍하도록 허용하시겠습니까?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>에서 오디오, 사진, 결제 정보, 비밀번호, 메시지 등 <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>에 표시되거나 해당 기기에서 재생되는 모든 항목에 액세스할 수 있습니다.<br/><br/>이 권한에 대한 액세스를 삭제할 때까지 <xliff:g id="APP_NAME_2">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> 기기로 앱을 스트리밍할 수 있습니다."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>의 앱을 스트리밍할 권한을 요청하고 있습니다."</string> <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string> <string name="summary_generic" msgid="1761976003668044801">"이 앱에서 휴대전화와 선택한 기기 간에 정보(예: 발신자 이름)를 동기화할 수 있게 됩니다."</string> <string name="consent_yes" msgid="8344487259618762872">"허용"</string> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index d74436f03d28..70bdf1f8336a 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүн тескөөгө уруксат бересизби?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"түзмөк"</string> <string name="summary_glasses" msgid="5469208629679579157">"Бул колдонмого <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> түзмөгүнө <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүндөгү колдонмолорду жана тутумдун функцияларын <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> түзмөгүндө алып ойнотууга уруксат бересизби?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздө көрүнгөн же ойнотулган бардык нерселерге, анын ичинде аудио, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөргө кире алат.<br/><br/>Бул уруксатты алып салмайынча, <xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g> түзмөгүндөгү колдонмолорду алып ойното алат."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> түзмөгүңүздөн колдонмолорду жана тутум функцияларын алып ойнотуу үчүн <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүнүн атынан уруксат сурап жатат"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздөгү ушул маалыматты көрүүгө уруксат бериңиз"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> сүрөттөрүн, медиа файлдарын жана билдирмелерин колдонууга уруксат сурап жатат"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздөгү колдонмолорду <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> түзмөгүнө алып ойнотууга уруксат бересизби?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> түзмөгүңүздө көрүнгөн же ойнотулган бардык нерселерге, анын ичинде аудио, сүрөттөр, төлөм маалыматы, сырсөздөр жана билдирүүлөргө кире алат.<br/><br/>Бул уруксатты алып салмайынча, <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> түзмөгүндөгү колдонмолорду алып ойното алат."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> түзмөгүңүздөн колдонмолорду алып ойнотуу үчүн <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүнүн атынан уруксат сурап жатат"</string> <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string> <string name="summary_generic" msgid="1761976003668044801">"Бул колдонмо маалыматты шайкештире алат, мисалы, чалып жаткан кишинин атын телефон жана тандалган түзмөк менен шайкештирет"</string> <string name="consent_yes" msgid="8344487259618762872">"Ооба"</string> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml index 8a6a564c39a7..7a5f347073a4 100644 --- a/packages/CompanionDeviceManager/res/values-lt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> valdyti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"įrenginio"</string> <string name="summary_glasses" msgid="5469208629679579157">"Šiai programai bus leidžiama pasiekti toliau nurodytus leidimus jūsų <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Leisti programai <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> srautu perduoti <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> programas ir sistemos funkcijas į <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ galės pasiekti visą jūsų <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> matomą ar leidžiamą turinį, įskaitant garso įrašus, nuotraukas, mokėjimo informaciją, slaptažodžius ir pranešimus.<br/><br/>Programa „<xliff:g id="APP_NAME_1">%1$s</xliff:g>“ galės perduoti srautu programas į „<xliff:g id="DEVICE_NAME">%3$s</xliff:g>“, kol pašalinsite prieigą prie šio leidimo."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas ir sistemos funkcijas iš jūsų <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Leisti programai <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pasiekti šią informaciją iš jūsų <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų pasiekti <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> nuotraukas, mediją ir pranešimus"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Leisti programai <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> srautu perduoti <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> programas į <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“ galės pasiekti visą „<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>“ matomą ar leidžiamą turinį, įskaitant garso įrašus, nuotraukas, mokėjimo informaciją, slaptažodžius ir pranešimus.<br/><br/>Programa „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ galės perduoti srautu programas į „<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>“, kol pašalinsite prieigą prie šio leidimo."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas iš jūsų <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string> <string name="summary_generic" msgid="1761976003668044801">"Ši programa galės sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, su jūsų telefonu ir pasirinktu įrenginiu"</string> <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml index 26f0968d9e50..2d79d530ded9 100644 --- a/packages/CompanionDeviceManager/res/values-lv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt ierīcei <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ierīce"</string> <string name="summary_glasses" msgid="5469208629679579157">"Šī lietotne drīkstēs piekļūt norādītajām <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> atļaujām."</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> straumēt <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> lietotnes un sistēmas funkcijas ierīcē <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> varēs piekļūt visam <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ekrānā parādītajam vai atskaņotajam saturam, tostarp audio, fotoattēliem, maksājumu informācijai, parolēm un ziņojumiem.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> varēs straumēt lietotnes ierīcē <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, līdz noņemsiet piekļuvi šai atļaujai."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju <xliff:g id="DEVICE_NAME">%2$s</xliff:g> vārdā straumēt lietotnes un sistēmas funkcijas no jūsu ierīces <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt šai informācijai no jūsu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju piekļūt jūsu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> fotoattēliem, multivides saturam un paziņojumiem šīs ierīces vārdā: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>."</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> straumēt <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> lietotnes ierīcē <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> varēs piekļūt visam <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ekrānā parādītajam vai atskaņotajam saturam, tostarp audio, fotoattēliem, maksājumu informācijai, parolēm un ziņojumiem.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> varēs straumēt lietotnes ierīcē <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, līdz noņemsiet piekļuvi šai atļaujai."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju <xliff:g id="DEVICE_NAME">%2$s</xliff:g> vārdā straumēt lietotnes no jūsu ierīces <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>."</string> <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string> <string name="summary_generic" msgid="1761976003668044801">"Šī lietotne varēs sinhronizēt informāciju (piemēram, zvanītāja vārdu) starp jūsu tālruni un izvēlēto ierīci"</string> <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml index 2439adc48bb8..1a19f224c998 100644 --- a/packages/CompanionDeviceManager/res/values-ml/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? മാനേജ് ചെയ്യാൻ, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കുക"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ഉപകരണം"</string> <string name="summary_glasses" msgid="5469208629679579157">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> എന്നതിൽ ഇനിപ്പറയുന്ന അനുമതികൾ ആക്സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കും"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിന്റെ ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളും <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> എന്നതിലേക്ക് സ്ട്രീം ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കണോ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"ഓഡിയോ, ഫോട്ടോകൾ, പാസ്വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിൽ ദൃശ്യമാകുന്നതോ പ്ലേ ചെയ്യുന്നതോ എല്ലാ എല്ലാത്തിലേക്കും <xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്നതിന് ആക്സസ് ഉണ്ടായിരിക്കും.<br/><br/>നിങ്ങൾ ഈ അനുമതിയിലേക്കുള്ള ആക്സസ് നീക്കം ചെയ്യുന്നത് വരെ <xliff:g id="APP_NAME_1">%1$s</xliff:g> എന്നതിന് <xliff:g id="DEVICE_NAME">%3$s</xliff:g> എന്നതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാനാകും."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> എന്നതിൽ നിന്ന് ആപ്പുകളും സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിന്റെ പേരിൽ <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്സസ് ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കുക"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> എന്നതിലെ ഫോട്ടോകൾ, മീഡിയ, അറിയിപ്പുകൾ എന്നിവ ആക്സസ് ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്ന ഉപകരണത്തിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> എന്നതിന്റെ ആപ്പുകൾ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> എന്നതിലേക്ക് സ്ട്രീം ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കണോ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ഓഡിയോ, ഫോട്ടോകൾ, പേയ്മെന്റ് വിവരങ്ങൾ, പാസ്വേഡുകൾ, സന്ദേശങ്ങൾ എന്നിവ ഉൾപ്പെടെ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> എന്നതിൽ ദൃശ്യമാകുന്നതോ പ്ലേ ചെയ്യുന്നതോ എല്ലാ എല്ലാത്തിലേക്കും <xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്നതിന് ആക്സസ് ഉണ്ടായിരിക്കും.<br/><br/>നിങ്ങൾ ഈ അനുമതിയിലേക്കുള്ള ആക്സസ് നീക്കം ചെയ്യുന്നത് വരെ <xliff:g id="APP_NAME_2">%1$s</xliff:g> എന്നതിന് <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> എന്നതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാനാകും."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> എന്നതിൽ നിന്ന് ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിന്റെ പേരിൽ <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string> <string name="summary_generic" msgid="1761976003668044801">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ നിങ്ങളുടെ ഫോണിനും തിരഞ്ഞെടുത്ത ഉപകരണത്തിനും ഇടയിൽ സമന്വയിപ്പിക്കുന്നതിന് ഈ ആപ്പിന് കഴിയും"</string> <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string> diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml index 6a7e10ef8212..5ac1e56e4331 100644 --- a/packages/CompanionDeviceManager/res/values-mr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापित करण्याची अनुमती द्यायची आहे?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"डिव्हाइस"</string> <string name="summary_glasses" msgid="5469208629679579157">"या अॅपला तुमच्या <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> वर या परवानग्या अॅक्सेस करण्याची अनुमती दिली जाईल"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> अॅप्स आणि सिस्टीमची वैशिष्ट्ये <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>वर स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ला ऑडिओ, फोटो, पेमेंट माहिती, पासवर्ड आणि मेसेज यांसह तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> वर दिसणाऱ्या किंवा प्ले होणाऱ्या सर्व गोष्टींचा अॅक्सेस असेल.<br/><br/>तुम्ही या परवानगीचा अॅक्सेस काढून टाकेपर्यंत <xliff:g id="APP_NAME_1">%1$s</xliff:g> हे ॲप्स <xliff:g id="DEVICE_NAME">%3$s</xliff:g> वर स्ट्रीम करू शकेल."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> वरून अॅप्स आणि सिस्टीम वैशिष्ट्ये स्ट्रीम करण्यासाठी <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला ही माहिती तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> वरून अॅक्सेस करण्यासाठी अनुमती द्या"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"तुमच्या <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> मधील फोटो, मीडिया आणि नोटिफिकेशन अॅक्सेस करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ला अॅप्स <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>वर स्ट्रीम करण्याची अनुमती द्यायची आहे का?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ला ऑडिओ, फोटो, पेमेंट माहिती, पासवर्ड आणि मेसेज यांसह तुमच्या <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> वर दिसणाऱ्या किंवा प्ले होणाऱ्या सर्व गोष्टींचा अॅक्सेस असेल.<br/><br/>तुम्ही या परवानगीचा अॅक्सेस काढून टाकेपर्यंत <xliff:g id="APP_NAME_2">%1$s</xliff:g> हे <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> वर ॲप्स स्ट्रीम करू शकेल."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> वरून अॅप्स आणि सिस्टीम वैशिष्ट्ये स्ट्रीम करण्यासाठी <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string> <string name="summary_generic" msgid="1761976003668044801">"हे ॲप तुमचा फोन आणि निवडलेल्या डिव्हाइसदरम्यान कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करू शकेल"</string> <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml index dea62f69fa9c..bb4e7c55716e 100644 --- a/packages/CompanionDeviceManager/res/values-my/strings.xml +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ကို <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား စီမံခွင့်ပြုမလား။"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"စက်"</string> <string name="summary_glasses" msgid="5469208629679579157">"သင့် <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ၏ အက်ပ်နှင့် စနစ်တူးလ်များကို <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> တွင် တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> သည် အသံ၊ ဓာတ်ပုံ၊ ငွေချေအချက်အလက်၊ စကားဝှက်နှင့် မက်ဆေ့ဂျ်များအပါအဝင် သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> တွင် မြင်နိုင်သော (သို့) ဖွင့်ထားသော အရာအားလုံးကို သုံးခွင့်ရှိပါမည်။<br/><br/>ဤခွင့်ပြုချက်သုံးခွင့်ကို သင်မဖယ်ရှားမချင်း <xliff:g id="APP_NAME_1">%1$s</xliff:g> သည် <xliff:g id="DEVICE_NAME">%3$s</xliff:g> တွင် အက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်ပါမည်။"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့် <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> မှ အက်ပ်များနှင့် စနစ်တူးလ်များကို တိုက်ရိုက်ဖွင့်ရန် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> မှ ဤအချက်အလက်ကို သုံးခွင့်ပြုမည်"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့် <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား သင့် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ၏ အက်ပ်များကို <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> တွင် တိုက်ရိုက်ဖွင့်ခွင့်ပြုမလား။"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> သည် အသံ၊ ဓာတ်ပုံ၊ စကားဝှက်နှင့် မက်ဆေ့ဂျ်များအပါအဝင် <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> တွင် မြင်နိုင်သော (သို့) ဖွင့်ထားသော အရာအားလုံးကို သုံးခွင့်ရှိပါမည်။<br/><br/>ဤခွင့်ပြုချက်သုံးခွင့်ကို သင်မဖယ်ရှားမချင်း <xliff:g id="APP_NAME_2">%1$s</xliff:g> သည် <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> တွင် အက်ပ်များကို တိုက်ရိုက်ဖွင့်နိုင်ပါမည်။"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့် <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> မှ အက်ပ်များကို တိုက်ရိုက်ဖွင့်ရန် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string> <string name="summary_generic" msgid="1761976003668044801">"ဤအက်ပ်သည် သင့်ဖုန်းနှင့် ရွေးထားသောစက်အကြား ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်နိုင်ပါမည်"</string> <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml index 9a40b6bb3d32..e8adbcd61910 100644 --- a/packages/CompanionDeviceManager/res/values-nb/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du la <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administrere <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"enheten"</string> <string name="summary_glasses" msgid="5469208629679579157">"Denne appen får disse tillatelsene på <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Vil du la <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> strømme apper og systemfunksjoner fra <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> kan se alt som vises eller spilles av på <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inkludert lyd, bilder, betalingsopplysninger, passord og meldinger.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> kan strømme apper til <xliff:g id="DEVICE_NAME">%3$s</xliff:g> frem til du fjerner tilgangen til denne tillatelsen."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper og systemfunksjoner fra <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> på vegne av <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Vil du gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilgang til denne informasjonen fra <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tilgang til bilder, medieinnhold og varsler fra <xliff:g id="DEVICE_NAME">%2$s</xliff:g> på vegne av <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vil du la <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> strømme apper fra <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> kan se som vises eller spilles av på <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inkludert lyd, bilder, passord og meldinger.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> kan strømme apper til <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> frem til du fjerner tilgangen til denne tillatelsen."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper fra <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> på vegne av <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> <string name="summary_generic" msgid="1761976003668044801">"Denne appen kan synkronisere informasjon som navnet til noen som ringer, mellom telefonen og den valgte enheten"</string> <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string> diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml index fdd011b4e4a6..6386057f41b4 100644 --- a/packages/CompanionDeviceManager/res/values-ne/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापन गर्ने अनुमति दिने हो?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"डिभाइस"</string> <string name="summary_glasses" msgid="5469208629679579157">"तपाईंको <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> मा यो एपलाई निम्न अनुमति दिइने छ:"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएका एप तथा सिस्टमका सुविधाहरू <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> मा स्ट्रिम गर्न दिने हो?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा देखिने वा प्ले गरिने अडियो, फोटो, भुक्तानीसम्बन्धी जानकारी, पासवर्ड र म्यासेजलगायतका सबै कुरा एक्सेस गर्न सक्ने छ।<br/><br/>तपाईंले यो अनुमति रद्द नगरेसम्म <xliff:g id="APP_NAME_1">%1$s</xliff:g> ले एपहरू <xliff:g id="DEVICE_NAME">%3$s</xliff:g> मा स्ट्रिम गर्न पाइराख्ने छ।"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> बाट एप र सिस्टमका अन्य सुविधाहरू स्ट्रिम गर्ने अनुमति माग्दै छ"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएको यो जानकारी एक्सेस गर्ने अनुमति दिनुहोस्"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> मा भएका फोटो, मिडिया र सूचनाहरू एक्सेस गर्ने अनुमति माग्दै छ"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई तपाईंको <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> मा भएका एपहरू <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> मा स्ट्रिम गर्न दिने हो?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ले <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> मा देखिने वा प्ले गरिने अडियो, फोटो, भुक्तानीसम्बन्धी जानकारी, पासवर्ड र म्यासेजलगायतका सबै कुरा एक्सेस गर्न सक्ने छ।<br/><br/>तपाईंले यो अनुमति रद्द नगरेसम्म <xliff:g id="APP_NAME_2">%1$s</xliff:g> ले एपहरू <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> मा स्ट्रिम गर्न पाइराख्ने छ।"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट तपाईंको <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> बाट एपहरू स्ट्रिम गर्ने अनुमति माग्दै छ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string> <string name="summary_generic" msgid="1761976003668044801">"यो एपले तपाईंको फोन र तपाईंले छनौट गर्ने डिभाइसका बिचमा कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्न सक्ने छ।"</string> <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index d71e8c16f182..58c7d4f7c31c 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestaan <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te beheren?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"apparaat"</string> <string name="summary_glasses" msgid="5469208629679579157">"Deze app krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestaan om apps en systeemfuncties van je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> naar <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> te streamen?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> krijgt toegang tot alles wat zichtbaar is of wordt afgespeeld op je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, waaronder audio, foto\'s, betalingsgegevens, wachtwoorden en berichten.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> kan apps naar <xliff:g id="DEVICE_NAME">%3$s</xliff:g> streamen totdat je dit recht verwijdert."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en systeemfuncties te streamen vanaf je <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot deze informatie op je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>?"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestaan om apps van je <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> naar <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> te streamen?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> krijgt toegang tot alles wat zichtbaar is of wordt afgespeeld op <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, waaronder audio, foto\'s, wachtwoorden en berichten.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> kan apps naar <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> streamen totdat je dit recht verwijdert."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps te streamen vanaf je <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string> <string name="summary_generic" msgid="1761976003668044801">"Deze app kan informatie, zoals de naam van iemand die belt, synchroniseren tussen je telefoon en het gekozen apparaat"</string> <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string> diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml index 1e8ed0bbb661..ffba68f6eeb1 100644 --- a/packages/CompanionDeviceManager/res/values-or/strings.xml +++ b/packages/CompanionDeviceManager/res/values-or/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ଡିଭାଇସ"</string> <string name="summary_glasses" msgid="5469208629679579157">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ର ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ରେ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦେବେ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"ଅଡିଓ, ଫଟୋ, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ର ଆକ୍ସେସ ରହିବ।<br/><br/>ଆପଣ ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ କାଢ଼ି ନଦେବା ପର୍ଯ୍ୟନ୍ତ <xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g>ରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ ସକ୍ଷମ ହେବ।"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ରୁ ଆପ୍ସ ଏବଂ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ଅନୁରୋଧ କରୁଛି"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ର ଫଟୋ, ମିଡିଆ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କର <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ର ଆପ୍ସକୁ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>ରେ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦେବେ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ଅଡିଓ, ଫଟୋ, ପାସୱାର୍ଡ ଏବଂ ମେସେଜ ସମେତ <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>ରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ର ଆକ୍ସେସ ରହିବ।<br/><br/>ଆପଣ ଏହି ଅନୁମତିକୁ ଆକ୍ସେସ କାଢ଼ି ନଦେବା ପର୍ଯ୍ୟନ୍ତ <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>ରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ ସକ୍ଷମ ହେବ।"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ରୁ ଆପ୍ସ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ଅନୁରୋଧ କରୁଛି"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string> <string name="summary_generic" msgid="1761976003668044801">"ଆପଣଙ୍କ ଫୋନ ଏବଂ ବଛାଯାଇଥିବା ଡିଭାଇସ ମଧ୍ୟରେ, କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏହି ଆପ ସକ୍ଷମ ହେବ"</string> <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string> diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml index 94a858496f76..463a02bab3b0 100644 --- a/packages/CompanionDeviceManager/res/values-pa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"ਡੀਵਾਈਸ"</string> <string name="summary_glasses" msgid="5469208629679579157">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀਆਂ ਐਪਾਂ ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> \'ਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਅਤੇ ਸਿਸਟਮ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦੀ ਵਰਤੋਂ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।<br/><br/>ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨੂੰ ਹਟਾ ਨਹੀਂ ਦਿੰਦੇ, ਉਦੋਂ ਤੱਕ <xliff:g id="APP_NAME_1">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%3$s</xliff:g> \'ਤੇ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗੀ।"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ਤੋਂ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ਦੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ਦੀਆਂ ਐਪਾਂ ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> \'ਤੇ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਕੋਲ ਆਡੀਓ, ਫ਼ੋਟੋਆਂ, ਭੁਗਤਾਨ ਜਾਣਕਾਰੀ, ਪਾਸਵਰਡਾਂ ਅਤੇ ਸੁਨੇਹਿਆਂ ਸਮੇਤ, <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> \'ਤੇ ਦਿਖਾਈ ਦੇਣ ਵਾਲੀ ਜਾਂ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਕਿਸੇ ਵੀ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੋਵੇਗੀ।<br/><br/>ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਤੱਕ ਪਹੁੰਚ ਨੂੰ ਹਟਾ ਨਹੀਂ ਦਿੰਦੇ, ਉਦੋਂ ਤੱਕ <xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> \'ਤੇ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰ ਸਕੇਗੀ।"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ਤੋਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string> <string name="summary_generic" msgid="1761976003668044801">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ ਅਤੇ ਚੁਣੇ ਗਏ ਡੀਵਾਈਸ ਵਿਚਕਾਰ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰ ਸਕੇਗੀ"</string> <string name="consent_yes" msgid="8344487259618762872">"ਆਗਿਆ ਦਿਓ"</string> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index 949957dae84e..dc7977d4e429 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Zezwolić na dostęp aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> do urządzenia <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"urządzenie"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aplikacja będzie miała dostęp do tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Zezwolić aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na strumieniowanie aplikacji i funkcji systemowych na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na urządzenie <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> będzie miała dostęp do wszystkiego, co jest widoczne i odtwarzane na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, w tym do dźwięku, zdjęć, danych do płatności, haseł i wiadomości.<br/><br/>Aplikacja <xliff:g id="APP_NAME_1">%1$s</xliff:g> będzie mogła strumieniować aplikacje na urządzenie <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, dopóki nie usuniesz dostępu do tego uprawnienia."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o pozwolenie na strumieniowanie aplikacji i funkcji systemowych z urządzenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Zezwól aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na dostęp do tych informacji na Twoim <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o uprawnienia dotyczące dostępu do zdjęć, multimediów i powiadomień na <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Zezwolić aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na strumieniowanie aplikacji na <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> na urządzenie <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Aplikacja <xliff:g id="APP_NAME_0">%1$s</xliff:g> będzie miała dostęp do wszystkiego, co jest widoczne i odtwarzane na <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, w tym do dźwięku, zdjęć, haseł i wiadomości.<br/><br/>Aplikacja <xliff:g id="APP_NAME_2">%1$s</xliff:g> będzie mogła strumieniować aplikacje na urządzenie <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, dopóki nie usuniesz dostępu do tego uprawnienia."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o pozwolenie na strumieniowanie aplikacji z urządzenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string> <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak imię i nazwisko osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string> <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml index cce096871838..88cf563c056f 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gerencie o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça streaming dos apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.<br/><br/>O app <xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse essas informações do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça streaming dos aplicativos do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.<br/><br/>O app <xliff:g id="APP_NAME_2">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml index 1443b137a9a6..34034f003eb6 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça a gestão do dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"Esta app vai poder aceder a estas autorizações no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Permitir que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça stream das apps e funcionalidades do sistema do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o dispositivo <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> vai ter acesso a tudo o que seja visível ou reproduzido no seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.<br/><br/>A app <xliff:g id="APP_NAME_1">%1$s</xliff:g> vai poder fazer stream de apps para o dispositivo <xliff:g id="DEVICE_NAME">%3$s</xliff:g> até remover o acesso a esta autorização."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer stream de apps e funcionalidades do sistema a partir do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para aceder às fotos, ao conteúdo multimédia e às notificações do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permitir que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça stream das apps do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o dispositivo <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> vai ter acesso a tudo o que seja visível ou reproduzido no dispositivo <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluindo áudio, fotos, informações de pagamento, palavras-passe e mensagens.<br/><br/>A app <xliff:g id="APP_NAME_2">%1$s</xliff:g> vai poder fazer stream de apps para o dispositivo <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> até remover o acesso a esta autorização."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer stream de apps do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, entre o telemóvel e o dispositivo escolhido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml index cce096871838..88cf563c056f 100644 --- a/packages/CompanionDeviceManager/res/values-pt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gerencie o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> <string name="summary_glasses" msgid="5469208629679579157">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça streaming dos apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.<br/><br/>O app <xliff:g id="APP_NAME_1">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e recursos do sistema do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse essas informações do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para acessar fotos, mídia e notificações do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça streaming dos aplicativos do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para o <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> terá acesso a tudo que estiver visível ou for aberto no <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, incluindo áudios, fotos, informações de pagamento, senhas e mensagens.<br/><br/>O app <xliff:g id="APP_NAME_2">%1$s</xliff:g> poderá fazer streaming de aplicativos para o <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> até que você remova o acesso a essa permissão."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps do seu <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index 528a73fd607d..002c55211819 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permiți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să gestioneze <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"dispozitiv"</string> <string name="summary_glasses" msgid="5469208629679579157">"Aplicația va putea să acceseze următoarele permisiuni pe <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Permiți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să redea în stream aplicații și funcții de sistem de pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pe <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> va avea acces la tot conținutul vizibil sau redat pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, inclusiv conținut audio, fotografii, informații de plată, parole și mesaje.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> va putea să redea în stream aplicații pe <xliff:g id="DEVICE_NAME">%3$s</xliff:g> până când elimini accesul la această permisiune."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a reda în stream aplicații și funcții de sistem de pe <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Permiți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să redea în stream aplicații de pe <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pe <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> va avea acces la tot conținutul vizibil sau redat pe <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, inclusiv conținut audio, fotografii, informații de plată, parole și mesaje.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> va putea să redea în stream aplicații pe <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> până când elimini accesul la această permisiune."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a reda în stream aplicații de pe <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string> <string name="summary_generic" msgid="1761976003668044801">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, între telefonul tău și dispozitivul ales"</string> <string name="consent_yes" msgid="8344487259618762872">"Permite"</string> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml index 6d21bebc5012..6f06a2a15820 100644 --- a/packages/CompanionDeviceManager/res/values-ru/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Разрешить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управлять устройством <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"устройстве"</string> <string name="summary_glasses" msgid="5469208629679579157">"Это приложение получит указанные разрешения на вашем устройстве (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)."</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Разрешить устройству <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> транслировать приложения и системные функции с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) на устройство <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"У приложения \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будет доступ ко всему, что показывается или воспроизводится на устройстве (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>), включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.<br/><br/>Приложение \"<xliff:g id="APP_NAME_1">%1$s</xliff:g>\" сможет транслировать приложения на устройство \"<xliff:g id="DEVICE_NAME">%3$s</xliff:g>\", пока вы не отзовете разрешение."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения и системные функции с устройства (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Разрешить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ к этой информации с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)?"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение на доступ к фотографиям, медиаконтенту и уведомлениям на устройстве (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)."</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Разрешить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> транслировать приложения с вашего устройства (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) на устройство <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"У приложения \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" будет доступ ко всему, что показывается или воспроизводится на устройстве \"<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>\", включая аудиофайлы, фотографии, платежные данные, пароли и сообщения.<br/><br/>Приложение \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" сможет транслировать приложения на устройство \"<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>\", пока вы не отзовете разрешение."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения с устройства (<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>)."</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> <string name="summary_generic" msgid="1761976003668044801">"Приложение сможет синхронизировать информацию между телефоном и выбранным устройством, например данные из журнала звонков."</string> <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml index e8b119759f7b..c4b29661e170 100644 --- a/packages/CompanionDeviceManager/res/values-si/strings.xml +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> කළමනා කිරීමට ඉඩ දෙන්න ද?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"උපාංගය"</string> <string name="summary_glasses" msgid="5469208629679579157">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> මත මෙම අවසර වෙත ප්රවේශ වීමට ඉඩ දෙනු ඇත"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි යෙදුම් සහ පද්ධති විශේෂාංග <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> වෙත ප්රවාහ කිරීමට <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඉඩ දෙන්න ද?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> හට ශ්රව්ය, ඡායාරූප, ගෙවීම් තොරතුරු, මුරපද සහ පණිවිඩ ඇතුළුව ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි දෘශ්යමාන හෝ වාදනය වන ඕනෑම දෙයකට ප්රවේශය ඇත.<br/><br/>ඔබ මෙම අවසරයට ප්රවේශය ඉවත් කරන තෙක් <xliff:g id="APP_NAME_1">%1$s</xliff:g> හට <xliff:g id="DEVICE_NAME">%3$s</xliff:g> වෙත යෙදුම් ප්රවාහ කිරීමට හැකි වනු ඇත."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> වෙතින් යෙදුම් සහ පද්ධති විශේෂාංග ප්රවාහ කිරීමට අවසර ඉල්ලා සිටියි"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙතින් මෙම තොරතුරු වෙත ප්රවේශ වීමට ඉඩ දෙන්න"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> හි ඡායාරූප, මාධ්ය, සහ දැනුම්දීම් වෙත ප්රවේශ වීමට අවසරය ඉල්ලමින් සිටියි"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"ඔබේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> හි යෙදුම් <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> වෙත ප්රවාහ කිරීමට <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඉඩ දෙන්න ද?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> හට ශ්රව්ය, ඡායාරූප, ගෙවීම් තොරතුරු, මුරපද සහ පණිවිඩ ඇතුළුව <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> හි දෘශ්යමාන හෝ වාදනය වන ඕනෑම දෙයකට ප්රවේශය ඇත.<br/><br/>ඔබ මෙම අවසරයට ප්රවේශය ඉවත් කරන තෙක් <xliff:g id="APP_NAME_2">%1$s</xliff:g> හට <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> වෙත යෙදුම් ප්රවාහ කිරීමට හැකි වනු ඇත."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> වෙතින් යෙදුම් ප්රවාහ කිරීමට අවසර ඉල්ලා සිටියි"</string> <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string> <string name="summary_generic" msgid="1761976003668044801">"මෙම යෙදුමට ඔබේ දුරකථනය සහ තෝරා ගත් උපාංගය අතර, අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට හැකි වනු ඇත"</string> <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index 41afcd525e42..bf96c5c26bae 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> spravovať zariadenie <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"zariadenie"</string> <string name="summary_glasses" msgid="5469208629679579157">"Táto aplikácia bude mať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovať aplikácie a systémové funkcie zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zariadenia <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mať prístup k všetkému, čo sa zobrazuje alebo prehráva v zariadení <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> vrátane zvuku, fotiek, platobných údajov, hesiel a správ.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> bude môcť streamovať aplikácie do zariadenia <xliff:g id="DEVICE_NAME">%3$s</xliff:g>, kým prístup k tomuto povoleniu neodstránite."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje v mene zariadenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie a systémové funkcie z vášho zariadenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie na prístup k fotkám, médiám a upozorneniam zariadenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> streamovať aplikácie zo zariadenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> do zariadenia <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> bude mať prístup k všetkému, čo sa zobrazuje alebo prehráva v zariadení <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> vrátane zvuku, fotiek, platobných údajov, hesiel a správ.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> bude môcť streamovať aplikácie do zariadenia <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>, kým prístup k tomuto povoleniu neodstránite."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje v mene zariadenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie z vášho zariadenia <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string> <string name="summary_generic" msgid="1761976003668044801">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, medzi telefónom a vybraným zariadením"</string> <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml index 3f1420b0ec8f..582e832aeff2 100644 --- a/packages/CompanionDeviceManager/res/values-sr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Желите ли да дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управља уређајем <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"уређај"</string> <string name="summary_glasses" msgid="5469208629679579157">"Овој апликацији ће бити дозвољено да приступа овим дозволама на уређају <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Желите да дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> стримује апликације и системске функције уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ће имати приступ свему што се види или пушта на уређају <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, укључујући звук, слике, информације о плаћању, лозинке и поруке.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> ће моћи да стримује апликације на <xliff:g id="DEVICE_NAME">%3$s</xliff:g> док не уклоните приступ овој дозволи."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да стримује апликације и системске функције са уређаја <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> приступа овим информацијама са уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да приступа сликама, медијском садржају и обавештењима са уређаја <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Желите да дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> стримује апликације уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> на <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ће имати приступ свему што се види или пушта на уређају <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, укључујући звук, слике, информације о плаћању, лозинке и поруке.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> ће моћи да стримује апликације на <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> док не уклоните приступ овој дозволи."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> тражи дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да стримује апликације са уређаја <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string> <string name="summary_generic" msgid="1761976003668044801">"Ова апликација ће моћи да синхронизује податке, попут имена особе која упућује позив, између телефона и одабраног уређаја"</string> <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index ca8fd2257922..6d623e50fcc5 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Ungependa kuruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> idhibiti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"kifaa"</string> <string name="summary_glasses" msgid="5469208629679579157">"Programu hii itaruhusiwa kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yako"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Ungependa kuruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> itiririshe programu na vipengele vya mfumo vya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako kwenye <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Programu ya <xliff:g id="APP_NAME_0">%1$s</xliff:g> itafikia chochote kinachoonekana au kuchezwa kwenye <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako, ikiwa ni pamoja na sauti, picha, maelezo ya malipo, manenosiri na ujumbe.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> itaweza kutiririsha programu kwenye <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hadi utakapoondoa ruhusa hii."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ili itiririshe programu na vipengele vya mfumo kwenye <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yako"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yako"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Ungependa kuruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> itiririshe programu za <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako kwenye <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Programu ya <xliff:g id="APP_NAME_0">%1$s</xliff:g> itafikia chochote kinachoonekana au kuchezwa kwenye <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, ikiwa ni pamoja na sauti, picha, maelezo ya malipo, manenosiri na ujumbe.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> itaweza kutiririsha programu kwenye <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hadi utakapoondoa ruhusa hii."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ili itiririshe programu kwenye <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yako"</string> <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string> <string name="summary_generic" msgid="1761976003668044801">"Programu hii itaweza kusawazisha maelezo, kama vile jina la mtu anayepiga simu, kati ya simu yako na kifaa ulichochagua"</string> <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml index 2d3f2f3a89be..34671f9a0433 100644 --- a/packages/CompanionDeviceManager/res/values-te/strings.xml +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ను మేనేజ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"పరికరం"</string> <string name="summary_glasses" msgid="5469208629679579157">"మీ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> యాప్లను, సిస్టమ్ ఫీచర్లను <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>కు స్ట్రీమ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"ఆడియో, ఫోటోలు, పేమెంట్ సమాచారం, పాస్వర్డ్లతో సహా మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా <xliff:g id="APP_NAME_0">%1$s</xliff:g>కు యాక్సెస్ ఉంటుంది.<br/><br/>మీరు ఈ అనుమతికి యాక్సెస్ను తీసివేసే వరకు <xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g>కు యాప్లను స్ట్రీమ్ చేయగలదు."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"మీ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> నుండి యాప్లను, సిస్టమ్ ఫీచర్లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించండి"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"మీ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> ఫోటోలను, మీడియాను, ఇంకా నోటిఫికేషన్లను యాక్సెస్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> యాప్లను <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>కు స్ట్రీమ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"ఆడియో, ఫోటోలు, పేమెంట్ సమాచారం, పాస్వర్డ్లతో సహా <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>లో కనిపించే లేదా ప్లే అయ్యే దేనికైనా <xliff:g id="APP_NAME_0">%1$s</xliff:g>కు యాక్సెస్ ఉంటుంది.<br/><br/>మీరు ఈ అనుమతికి యాక్సెస్ను తీసివేసే వరకు <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>కు యాప్లను స్ట్రీమ్ చేయగలదు."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"మీ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> నుండి యాప్లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string> <string name="summary_generic" msgid="1761976003668044801">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని ఈ యాప్ మీ ఫోన్ కు, ఎంచుకున్న పరికరానికీ మధ్య సింక్ చేయగలుగుతుంది"</string> <string name="consent_yes" msgid="8344487259618762872">"అనుమతించండి"</string> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml index c15718620d55..e74f96c7260f 100644 --- a/packages/CompanionDeviceManager/res/values-th/strings.xml +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> จัดการ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ไหม"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"อุปกรณ์"</string> <string name="summary_glasses" msgid="5469208629679579157">"แอปนี้จะได้รับสิทธิ์ดังต่อไปนี้ใน<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ของคุณ"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> สตรีมแอปและฟีเจอร์ของระบบใน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณไปยัง <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ไหม"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> จะมีสิทธิ์เข้าถึงทุกอย่างที่ปรากฏหรือเล่นบน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ซึ่งรวมถึงเสียง รูปภาพ ข้อมูลการชำระเงิน รหัสผ่าน และข้อความ<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> จะสามารถสตรีมแอปไปยัง <xliff:g id="DEVICE_NAME">%3$s</xliff:g> ได้จนกว่าคุณจะนำการให้สิทธิ์นี้ออก"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อสตรีมแอปและฟีเจอร์ของระบบจาก<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ของคุณ"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> เข้าถึงข้อมูลนี้จาก<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณ"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อเข้าถึงรูปภาพ สื่อ และการแจ้งเตือนใน<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ของคุณ"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> สตรีมแอปใน<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ของคุณไปยัง <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> ไหม"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> จะมีสิทธิ์เข้าถึงทุกอย่างที่ปรากฏหรือเล่นบน <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> ซึ่งรวมถึงเสียง รูปภาพ ข้อมูลการชำระเงิน รหัสผ่าน และข้อความ<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> จะสามารถสตรีมแอปไปยัง <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> ได้จนกว่าคุณจะนำการให้สิทธิ์นี้ออก"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อสตรีมแอปจาก<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>ของคุณ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string> <string name="summary_generic" msgid="1761976003668044801">"แอปนี้จะสามารถซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา ระหว่างโทรศัพท์ของคุณและอุปกรณ์ที่เลือกไว้ได้"</string> <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml index ffa91b19d83f..ce907f731bf2 100644 --- a/packages/CompanionDeviceManager/res/values-tl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na pamahalaan ang <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> <string name="summary_glasses" msgid="5469208629679579157">"Papayagan ang app na ito na ma-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-stream ang mga app at feature ng system ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>’ sa <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"Magkakaroon ng access ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> sa kahit anong nakikita o pine-play sa iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, kasama ang audio, mga larawan, impormasyon sa pagbabayad, mga password, at mga mensahe.<br/><br/>Magagawa ng<xliff:g id="APP_NAME_1">%1$s</xliff:g> na mag-stream ng mga app sa <xliff:g id="DEVICE_NAME">%3$s</xliff:g> hanggang sa alisin mo ang access sa pahintulot na ito."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"Humihingi ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng pahintulot para sa <xliff:g id="DEVICE_NAME">%2$s</xliff:g> na mag-stream ng mga app at feature ng system mula sa iyong <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-access ang impormasyong ito sa iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"Humihiling ng pahintulot ang <xliff:g id="APP_NAME">%1$s</xliff:g> para sa iyong <xliff:g id="DEVICE_NAME">%2$s</xliff:g> na ma-access ang mga larawan, media, at notification ng <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> mo"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-stream ang mga app ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> sa <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"Magkakaroon ng access ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> sa kahit anong nakikita o pine-play sa <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, kasama ang audio, mga larawan, impormasyon sa pagbabayad, mga password, at mga mensahe.<br/><br/>Magagawa ng <xliff:g id="APP_NAME_2">%1$s</xliff:g> na mag-stream ng mga app sa <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> hanggang sa alisin mo ang access sa pahintulot na ito."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"Humihingi ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng pahintulot para sa <xliff:g id="DEVICE_NAME">%2$s</xliff:g> na mag-stream ng mga app mula sa iyong <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="1761976003668044801">"Magagawa ng app na ito na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, sa pagitan ng iyong telepono at ng napiling device"</string> <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string> diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml index 64732e814d00..24fd82795dbb 100644 --- a/packages/CompanionDeviceManager/res/values-ur/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کا نظم کرنے کی اجازت دیں؟"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"آلہ"</string> <string name="summary_glasses" msgid="5469208629679579157">"اس ایپ کو آپ کے <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی ایپس اور سسٹم کی خصوصیات کو <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> پر سلسلہ بندی کرنے کی اجازت دیں؟"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، ادائیگی کی معلومات، پاس ورڈز اور پیغامات۔;lt;br/><br/&gt&<xliff:g id="APP_NAME_1">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%3$s</xliff:g> پر اس وقت تک ایپس کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> سے ایپس اور سسٹم کی خصوصیات کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> سے ان معلومات تک رسائی حاصل کرنے کی اجازت دیں"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> کی تصاویر، میڈیا اور اطلاعات تک رسائی کی اجازت کی درخواست کر رہی ہے"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی ایپس کو <xliff:g id="DEVICE_NAME">%3$s</xliff:g> پر سلسلہ بندی کرنے کی اجازت دیں؟"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> کو <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> پر دکھائی دینے والی یا چلائی جانے والی کسی بھی چیز تک رسائی حاصل ہوگی، بشمول آڈیو، تصاویر، ادائیگی کی معلومات، پاس ورڈز اور پیغامات۔<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> پر اس وقت تک ایپس کی سلسلہ بندی کر سکے گی جب تک آپ اس اجازت تک رسائی کو ہٹا نہیں دیتے۔"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے آپ کے <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> سے ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string> <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string> <string name="summary_generic" msgid="1761976003668044801">"یہ ایپ آپ کے فون اور منتخب کردہ آلے کے درمیان معلومات، جیسے کسی کال کرنے والے کے نام، کی مطابقت پذیری کر سکے گی"</string> <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml index 4b0c48a181b0..3335551341bd 100644 --- a/packages/CompanionDeviceManager/res/values-uz/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> qurilmasini boshqarish uchun ruxsat berilsinmi?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"qurilma"</string> <string name="summary_glasses" msgid="5469208629679579157">"Bu ilova <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> qurilmasida quyidagi ruxsatlarni oladi"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>dagi ilovalar va tizim funksiyalarini <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> qurilmasiga striming qilishiga ruxsat berasizmi?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>da koʻrinadigan yoki ijro etiladigan hamma narsaga, jumladan, audio, rasmlar, toʻlov axboroti, parollar va xabarlarga kirish huquqini oladi.<br/><br/>Bu ruxsatni olib tashlamaguningizcha, <xliff:g id="APP_NAME_1">%1$s</xliff:g> ilovalarni <xliff:g id="DEVICE_NAME">%3$s</xliff:g> qurilmasiga striming qila oladi."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> orqali ilovalar va tizim funksiyalarini uzatish uchun ruxsat olmoqchi"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>dagi ushbu maʼlumot uchun ruxsat bering"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g>dagi suratlar, media va bildirishnomalarga kirish uchun ruxsat soʻramoqda"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>dagi ilovalarni <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> qurilmasiga striming qilishiga ruxsat berasizmi?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>da koʻrinadigan yoki ijro etiladigan hamma narsaga, jumladan, audio, rasmlar, parollar va xabarlarga kirish huquqini oladi.<br/><br/>Bu ruxsatni olib tashlamaguningizcha, <xliff:g id="APP_NAME_2">%1$s</xliff:g> ilovalarni <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> qurilmasiga striming qila oladi."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nomidan <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> orqali ilovalarni uzatish uchun ruxsat olmoqchi"</string> <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string> <string name="summary_generic" msgid="1761976003668044801">"Bu ilova telefoningiz va tanlangan qurilmada chaqiruvchining ismi kabi maʼlumotlarni sinxronlay oladi"</string> <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml index 0b65172e9980..7f6d5b199d3a 100644 --- a/packages/CompanionDeviceManager/res/values-vi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> quản lý <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"thiết bị"</string> <string name="summary_glasses" msgid="5469208629679579157">"Ứng dụng này sẽ được phép dùng những quyền sau trên <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> của bạn"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truyền trực tuyến các ứng dụng và tính năng của hệ thống trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn đến <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sẽ có quyền truy cập vào mọi nội dung hiển thị hoặc phát trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn, bao gồm cả âm thanh, ảnh, thông tin thanh toán, mật khẩu và tin nhắn.<br/><br/><xliff:g id="APP_NAME_1">%1$s</xliff:g> sẽ có thể truyền trực tuyến các ứng dụng đến <xliff:g id="DEVICE_NAME">%3$s</xliff:g> cho đến khi bạn thu hồi quyền này."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_NAME">%2$s</xliff:g> để truyền trực tuyến các ứng dụng và tính năng của hệ thống từ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> của bạn"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truy cập vào thông tin này trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_NAME">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> của bạn"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truyền trực tuyến các ứng dụng trên <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> của bạn đến <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> sẽ có quyền truy cập vào mọi nội dung hiển thị hoặc phát trên <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, bao gồm cả âm thanh, ảnh, thông tin thanh toán, mật khẩu và tin nhắn.<br/><br/><xliff:g id="APP_NAME_2">%1$s</xliff:g> sẽ có thể truyền trực tuyến các ứng dụng đến <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> cho đến khi bạn thu hồi quyền này."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_NAME">%2$s</xliff:g> để truyền trực tuyến các ứng dụng từ <xliff:g id="DEVICE_TYPE">%3$s</xliff:g> của bạn"</string> <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string> <string name="summary_generic" msgid="1761976003668044801">"Ứng dụng này sẽ đồng bộ hoá thông tin (ví dụ: tên người gọi) giữa điện thoại của bạn và thiết bị bạn chọn"</string> <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml index 80e2d50b9735..c54c452d9d0f 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>管理<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"设备"</string> <string name="summary_glasses" msgid="5469208629679579157">"该应用将能获得您<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的以下权限"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>将您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上的应用和系统功能流式传输到<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>吗?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"“<xliff:g id="APP_NAME_0">%1$s</xliff:g>”将能够访问您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上显示或播放的任何内容,包括音频、照片、付款信息、密码和消息。<br/><br/>“<xliff:g id="APP_NAME_1">%1$s</xliff:g>”可将应用流式传输到“<xliff:g id="DEVICE_NAME">%3$s</xliff:g>”,除非您撤消此访问权限。"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”请求获得从您的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>流式传输应用和系统功能的权限"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>访问您<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>中的这项信息"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”请求访问您<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>上的照片、媒体内容和通知"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>将您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上的应用流式传输到<strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>吗?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"“<xliff:g id="APP_NAME_0">%1$s</xliff:g>”将能够访问“<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>”上显示或播放的任何内容,包括音频、照片、付款信息、密码和消息。<br/><br/>“<xliff:g id="APP_NAME_2">%1$s</xliff:g>”可将应用流式传输到“<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>”,除非您撤消此访问权限。"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表“<xliff:g id="DEVICE_NAME">%2$s</xliff:g>”请求获得从您的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>流式传输应用的权限"</string> <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string> <string name="summary_generic" msgid="1761976003668044801">"此应用将能在您的手机和所选设备之间同步信息,例如来电者的姓名"</string> <string name="consent_yes" msgid="8344487259618762872">"允许"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index cfb14220d07b..e47dfa005713 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"裝置"</string> <string name="summary_glasses" msgid="5469208629679579157">"此應用程式將可在<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上取得以下權限"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」串流<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>應用程式內容和系統功能至 <xliff:g id="DEVICE_NAME">%3$s</xliff:g> 嗎?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將能存取<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上顯示或播放的任何內容,包括音訊、相片、付款資料、密碼和訊息。<br/><br/>「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」將能串流應用程式內容至 <xliff:g id="DEVICE_NAME">%3$s</xliff:g>,直至你移除此存取權限為止。"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求權限,以便從你的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流應用程式內容和系統功能"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」在<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>上存取這項資料"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」要求權限,以便存取<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>上的相片、媒體和通知"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」串流<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>應用程式內容至 <xliff:g id="DEVICE_NAME">%3$s</xliff:g> 嗎?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將能存取「<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>」上顯示或播放的任何內容,包括音訊、相片、付款資料、密碼和訊息。<br/><br/>「<xliff:g id="APP_NAME_2">%1$s</xliff:g>」將能串流應用程式內容至 <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>,直至你移除此存取權限為止。"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求權限,以便從你的<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流應用程式內容"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> <string name="summary_generic" msgid="1761976003668044801">"此應用程式將可同步手機和所選裝置的資訊,例如來電者的名稱"</string> <string name="consent_yes" msgid="8344487259618762872">"允許"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml index 490d1bd7b556..b91024a66cce 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"裝置"</string> <string name="summary_glasses" msgid="5469208629679579157">"這個應用程式將可取得<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的這些權限"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>將<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>的應用程式和系統功能串流傳輸到 <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> 嗎?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將可存取<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>顯示或播放的所有內容,包括音訊、相片、付款資訊、密碼和訊息。<br/><br/>「<xliff:g id="APP_NAME_1">%1$s</xliff:g>」可將應用程式串流傳輸到 <xliff:g id="DEVICE_NAME">%3$s</xliff:g>,直到你移除這個權限為止。"</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求必要權限,以便從<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流傳輸應用程式和系統功能"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>中的這項資訊"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求必要權限,以便存取<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>上的相片、媒體和通知"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>將<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>的應用程式串流傳輸到 <strong><xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong> 嗎?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」將可存取 <xliff:g id="DEVICE_NAME_1">%3$s</xliff:g> 顯示或播放的所有內容,包括音訊、相片、付款資訊、密碼和訊息。<br/><br/>「<xliff:g id="APP_NAME_2">%1$s</xliff:g>」可將應用程式串流傳輸到 <xliff:g id="DEVICE_NAME_3">%3$s</xliff:g>,直到你移除這個權限為止。"</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 要求必要權限,以便從<xliff:g id="DEVICE_TYPE">%3$s</xliff:g>串流傳輸應用程式"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> <string name="summary_generic" msgid="1761976003668044801">"這個應用程式將可在手機和指定裝置間同步資訊,例如來電者名稱"</string> <string name="consent_yes" msgid="8344487259618762872">"允許"</string> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml index 3003fb84418c..160332e77c94 100644 --- a/packages/CompanionDeviceManager/res/values-zu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -25,23 +25,17 @@ <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vumela i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuthi ifinyelele i-<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_glasses" msgid="3506504967216601277">"idivayisi"</string> <string name="summary_glasses" msgid="5469208629679579157">"Le-app izovunyelwa ukufinyelela lezi zimvume ku-<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yakho"</string> - <!-- no translation found for title_app_streaming (1047090167914857893) --> - <skip /> - <!-- no translation found for summary_app_streaming (7990244299655610920) --> - <skip /> - <!-- no translation found for helper_summary_app_streaming (1872657107404139828) --> - <skip /> + <string name="title_app_streaming" msgid="1047090167914857893">"Vumela <strong>i-<xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuba isakaze ama-app e-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho nezakhi zesistimu <strong>ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_app_streaming" msgid="7990244299655610920">"I-<xliff:g id="APP_NAME_0">%1$s</xliff:g> izokwazi ukufinyelela kunoma yini ebonakalayo noma edlalwayo ku-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho, okuhlanganisa umsindo, izithombe, ulwazi lokukhokha, amaphasiwedi, nemilayezo.<br/><br/>I-<xliff:g id="APP_NAME_1">%1$s</xliff:g> izokwazi ukusakaza ama-app ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g> uze ususe ukufinyelela kule mvume."</string> + <string name="helper_summary_app_streaming" msgid="1872657107404139828">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ukuze isakaze ama-app nezakhi zesistimu ukusuka ku-<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yakho"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4782923323932440751">"Vumela <strong>i-<xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuze ifinyelele lolu lwazi ukusuka ku-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_summary_computer" msgid="2298803016482139668">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> yakho ukuze ifinyelele izithombe ze-<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yakho, imidiya nezaziso"</string> - <!-- no translation found for title_nearby_device_streaming (2727103756701741359) --> - <skip /> - <!-- no translation found for summary_nearby_device_streaming (70434958004946884) --> - <skip /> - <!-- no translation found for helper_summary_nearby_device_streaming (4712712177819370967) --> - <skip /> + <string name="title_nearby_device_streaming" msgid="2727103756701741359">"Vumela <strong>i-<xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuze isakaze ama-app e-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho <strong>ku-<xliff:g id="DEVICE_NAME">%3$s</xliff:g></strong>?"</string> + <string name="summary_nearby_device_streaming" msgid="70434958004946884">"I-<xliff:g id="APP_NAME_0">%1$s</xliff:g> izokwazi ukufinyelela kunoma yini ebonakalayo noma edlalwayo ku-<xliff:g id="DEVICE_NAME_1">%3$s</xliff:g>, okuhlanganisa umsindo, izithombe, ulwazi lokukhokha, amaphasiwedi, nemilayezo.<br/><br/>I-<xliff:g id="APP_NAME_2">%1$s</xliff:g> izokwazi ukusakaza ama-app ku-<xliff:g id="DEVICE_NAME_3">%3$s</xliff:g> uze ususe ukufinyelela kule mvume."</string> + <string name="helper_summary_nearby_device_streaming" msgid="4712712177819370967">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ukuze isakaze ama-app nezakhi ukusuka ku-<xliff:g id="DEVICE_TYPE">%3$s</xliff:g> yakho"</string> <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string> <string name="summary_generic" msgid="1761976003668044801">"Le app izokwazi ukuvumelanisa ulwazi, njengegama lomuntu othile ofonayo, phakathi kwefoni yakho nedivayisi ekhethiwe"</string> <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string> diff --git a/packages/CrashRecovery/adaptor/Android.bp b/packages/CrashRecovery/adaptor/Android.bp new file mode 100644 index 000000000000..df7c3dd90ec4 --- /dev/null +++ b/packages/CrashRecovery/adaptor/Android.bp @@ -0,0 +1,12 @@ +filegroup { + name: "crashrecovery-platform-adaptor-srcs", + srcs: select(soong_config_variable("ANDROID", "release_crashrecovery_module"), { + "true": [ + "postModularization/java/**/*.java", + ], + default: [ + "preModularization/java/**/*.java", + ], + }), + visibility: ["//frameworks/base:__subpackages__"], +} diff --git a/packages/CrashRecovery/adaptor/postModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java b/packages/CrashRecovery/adaptor/postModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java new file mode 100644 index 000000000000..b2d798e28c58 --- /dev/null +++ b/packages/CrashRecovery/adaptor/postModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.crashrecovery; + +import android.content.Context; + +import com.android.server.PackageWatchdog; +import com.android.server.SystemServiceManager; + +import java.util.List; + +/** + * This class mediates calls to hidden APIs in CrashRecovery module. + * This class is used when the CrashRecovery classes are moved to separate module. + * + * @hide + */ +public class CrashRecoveryAdaptor { + private static final String TAG = "CrashRecoveryAdaptor"; + private static final String CRASHRECOVERY_MODULE_LIFECYCLE_CLASS = + "com.android.server.crashrecovery.CrashRecoveryModule$Lifecycle"; + + /** Start CrashRecoveryModule LifeCycleService */ + public static void initializeCrashrecoveryModuleService( + SystemServiceManager mSystemServiceManager) { + mSystemServiceManager.startService(CRASHRECOVERY_MODULE_LIFECYCLE_CLASS); + } + + /** Does Nothing */ + public static void packageWatchdogNoteBoot(Context mSystemContext) { + // do nothing + } + + /** Does Nothing */ + public static void packageWatchdogWriteNow(Context mContext) { + // do nothing + } + + /** Does Nothing */ + public static void packageWatchdogOnPackagesReady(PackageWatchdog mPackageWatchdog) { + // do nothing + } + + /** Does Nothing */ + public static void rescuePartyRegisterHealthObserver(Context mSystemContext) { + // do nothing + } + + /** Does Nothing */ + public static void rescuePartyOnSettingsProviderPublished(Context mContext) { + // do nothing + } + + /** Does Nothing */ + public static void rescuePartyResetDeviceConfigForPackages(List<String> packageNames) { + // do nothing + } +} diff --git a/packages/CrashRecovery/adaptor/preModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java b/packages/CrashRecovery/adaptor/preModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java new file mode 100644 index 000000000000..74c647cb3a58 --- /dev/null +++ b/packages/CrashRecovery/adaptor/preModularization/java/com/android/server/crashrecovery/CrashRecoveryAdaptor.java @@ -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.server.crashrecovery; + +import android.content.Context; + +import com.android.server.PackageWatchdog; +import com.android.server.RescueParty; +import com.android.server.SystemServiceManager; + +import java.util.List; + +/** + * This class mediates calls to hidden APIs in CrashRecovery module. + * This class is used when CrashRecovery classes are still in platform. + * + * @hide + */ +public class CrashRecoveryAdaptor { + private static final String TAG = "CrashRecoveryAdaptor"; + + /** Start CrashRecoveryModule LifeCycleService */ + public static void initializeCrashrecoveryModuleService( + SystemServiceManager mSystemServiceManager) { + mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class); + } + + /** Forward calls to PackageWatchdog noteboot */ + public static void packageWatchdogNoteBoot(Context mSystemContext) { + PackageWatchdog.getInstance(mSystemContext).noteBoot(); + } + + /** Forward calls to PackageWatchdog writeNow */ + public static void packageWatchdogWriteNow(Context mContext) { + PackageWatchdog.getInstance(mContext).writeNow(); + } + + /** Forward calls to PackageWatchdog OnPackagesReady */ + public static void packageWatchdogOnPackagesReady(PackageWatchdog mPackageWatchdog) { + mPackageWatchdog.onPackagesReady(); + } + + /** Forward calls to RescueParty RegisterHealthObserver */ + public static void rescuePartyRegisterHealthObserver(Context mSystemContext) { + RescueParty.registerHealthObserver(mSystemContext); + } + + /** Forward calls to RescueParty OnSettingsProviderPublished */ + public static void rescuePartyOnSettingsProviderPublished(Context mContext) { + RescueParty.onSettingsProviderPublished(mContext); + } + + /** Forward calls to RescueParty ResetDeviceConfigForPackages */ + public static void rescuePartyResetDeviceConfigForPackages(List<String> packageNames) { + RescueParty.resetDeviceConfigForPackages(packageNames); + } +} diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp index 961b41f4a633..1c8440223fe2 100644 --- a/packages/CrashRecovery/services/Android.bp +++ b/packages/CrashRecovery/services/Android.bp @@ -1,54 +1,18 @@ -soong_config_module_type { - name: "platform_filegroup", - module_type: "filegroup", - config_namespace: "ANDROID", - bool_variables: [ - "crashrecovery_files_in_platform", - ], - properties: [ - "srcs", - ], -} - -platform_filegroup { +filegroup { name: "services-crashrecovery-sources", - soong_config_variables: { - // if this flag is enabled, then files are part of platform - crashrecovery_files_in_platform: { - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ":statslog-crashrecovery-java-gen", - ], - }, - }, + srcs: [ + ":crashrecovery-platform-adaptor-srcs", + ":statslog-crashrecovery-java-gen", + ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), { + "true": [], + default: ["platform/java/**/*.java"], + }), visibility: ["//frameworks/base:__subpackages__"], } -soong_config_module_type { - name: "module_filegroup", - module_type: "filegroup", - config_namespace: "ANDROID", - bool_variables: [ - "crashrecovery_files_in_module", - ], - properties: [ - "srcs", - ], -} - -module_filegroup { +filegroup { name: "services-crashrecovery-module-sources", - soong_config_variables: { - // if this flag is enabled, then files are part of module - crashrecovery_files_in_module: { - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ":statslog-crashrecovery-java-gen", - ], - }, - }, + srcs: ["module/java/**/*.java"], visibility: ["//packages/modules/CrashRecovery/service"], } diff --git a/packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java new file mode 100644 index 000000000000..da9a13961f79 --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/server/ExplicitHealthCheckController.java @@ -0,0 +1,447 @@ +/* + * 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.server; + +import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_HEALTH_CHECK_PASSED_PACKAGE; +import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES; +import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES; +import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig; + +import android.Manifest; +import android.annotation.MainThread; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.UserHandle; +import android.service.watchdog.ExplicitHealthCheckService; +import android.service.watchdog.IExplicitHealthCheckService; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +// TODO(b/120598832): Add tests +/** + * Controls the connections with {@link ExplicitHealthCheckService}. + */ +class ExplicitHealthCheckController { + private static final String TAG = "ExplicitHealthCheckController"; + private final Object mLock = new Object(); + private final Context mContext; + + // Called everytime a package passes the health check, so the watchdog is notified of the + // passing check. In practice, should never be null after it has been #setEnabled. + // To prevent deadlocks between the controller and watchdog threads, we have + // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class. + // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer. + @GuardedBy("mLock") @Nullable private Consumer<String> mPassedConsumer; + // Called everytime after a successful #syncRequest call, so the watchdog can receive packages + // supporting health checks and update its internal state. In practice, should never be null + // after it has been #setEnabled. + // To prevent deadlocks between the controller and watchdog threads, we have + // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class. + // It's easier to just NOT hold #mLock when calling into watchdog code on this consumer. + @GuardedBy("mLock") @Nullable private Consumer<List<PackageConfig>> mSupportedConsumer; + // Called everytime we need to notify the watchdog to sync requests between itself and the + // health check service. In practice, should never be null after it has been #setEnabled. + // To prevent deadlocks between the controller and watchdog threads, we have + // a lock invariant to ALWAYS acquire the PackageWatchdog#mLock before #mLock in this class. + // It's easier to just NOT hold #mLock when calling into watchdog code on this runnable. + @GuardedBy("mLock") @Nullable private Runnable mNotifySyncRunnable; + // Actual binder object to the explicit health check service. + @GuardedBy("mLock") @Nullable private IExplicitHealthCheckService mRemoteService; + // Connection to the explicit health check service, necessary to unbind. + // We should only try to bind if mConnection is null, non-null indicates we + // are connected or at least connecting. + @GuardedBy("mLock") @Nullable private ServiceConnection mConnection; + // Bind state of the explicit health check service. + @GuardedBy("mLock") private boolean mEnabled; + + ExplicitHealthCheckController(Context context) { + mContext = context; + } + + /** Enables or disables explicit health checks. */ + public void setEnabled(boolean enabled) { + synchronized (mLock) { + Slog.i(TAG, "Explicit health checks " + (enabled ? "enabled." : "disabled.")); + mEnabled = enabled; + } + } + + /** + * Sets callbacks to listen to important events from the controller. + * + * <p> Should be called once at initialization before any other calls to the controller to + * ensure a happens-before relationship of the set parameters and visibility on other threads. + */ + public void setCallbacks(Consumer<String> passedConsumer, + Consumer<List<PackageConfig>> supportedConsumer, Runnable notifySyncRunnable) { + synchronized (mLock) { + if (mPassedConsumer != null || mSupportedConsumer != null + || mNotifySyncRunnable != null) { + Slog.wtf(TAG, "Resetting health check controller callbacks"); + } + + mPassedConsumer = Objects.requireNonNull(passedConsumer); + mSupportedConsumer = Objects.requireNonNull(supportedConsumer); + mNotifySyncRunnable = Objects.requireNonNull(notifySyncRunnable); + } + } + + /** + * Calls the health check service to request or cancel packages based on + * {@code newRequestedPackages}. + * + * <p> Supported packages in {@code newRequestedPackages} that have not been previously + * requested will be requested while supported packages not in {@code newRequestedPackages} + * but were previously requested will be cancelled. + * + * <p> This handles binding and unbinding to the health check service as required. + * + * <p> Note, calling this may modify {@code newRequestedPackages}. + * + * <p> Note, this method is not thread safe, all calls should be serialized. + */ + public void syncRequests(Set<String> newRequestedPackages) { + boolean enabled; + synchronized (mLock) { + enabled = mEnabled; + } + + if (!enabled) { + Slog.i(TAG, "Health checks disabled, no supported packages"); + // Call outside lock + mSupportedConsumer.accept(Collections.emptyList()); + return; + } + + getSupportedPackages(supportedPackageConfigs -> { + // Notify the watchdog without lock held + mSupportedConsumer.accept(supportedPackageConfigs); + getRequestedPackages(previousRequestedPackages -> { + synchronized (mLock) { + // Hold lock so requests and cancellations are sent atomically. + // It is important we don't mix requests from multiple threads. + + Set<String> supportedPackages = new ArraySet<>(); + for (PackageConfig config : supportedPackageConfigs) { + supportedPackages.add(config.getPackageName()); + } + // Note, this may modify newRequestedPackages + newRequestedPackages.retainAll(supportedPackages); + + // Cancel packages no longer requested + actOnDifference(previousRequestedPackages, + newRequestedPackages, p -> cancel(p)); + // Request packages not yet requested + actOnDifference(newRequestedPackages, + previousRequestedPackages, p -> request(p)); + + if (newRequestedPackages.isEmpty()) { + Slog.i(TAG, "No more health check requests, unbinding..."); + unbindService(); + return; + } + } + }); + }); + } + + private void actOnDifference(Collection<String> collection1, Collection<String> collection2, + Consumer<String> action) { + Iterator<String> iterator = collection1.iterator(); + while (iterator.hasNext()) { + String packageName = iterator.next(); + if (!collection2.contains(packageName)) { + action.accept(packageName); + } + } + } + + /** + * Requests an explicit health check for {@code packageName}. + * After this request, the callback registered on {@link #setCallbacks} can receive explicit + * health check passed results. + */ + private void request(String packageName) { + synchronized (mLock) { + if (!prepareServiceLocked("request health check for " + packageName)) { + return; + } + + Slog.i(TAG, "Requesting health check for package " + packageName); + try { + mRemoteService.request(packageName); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to request health check for package " + packageName, e); + } + } + } + + /** + * Cancels all explicit health checks for {@code packageName}. + * After this request, the callback registered on {@link #setCallbacks} can no longer receive + * explicit health check passed results. + */ + private void cancel(String packageName) { + synchronized (mLock) { + if (!prepareServiceLocked("cancel health check for " + packageName)) { + return; + } + + Slog.i(TAG, "Cancelling health check for package " + packageName); + try { + mRemoteService.cancel(packageName); + } catch (RemoteException e) { + // Do nothing, if the service is down, when it comes up, we will sync requests, + // if there's some other error, retrying wouldn't fix anyways. + Slog.w(TAG, "Failed to cancel health check for package " + packageName, e); + } + } + } + + /** + * Returns the packages that we can request explicit health checks for. + * The packages will be returned to the {@code consumer}. + */ + private void getSupportedPackages(Consumer<List<PackageConfig>> consumer) { + synchronized (mLock) { + if (!prepareServiceLocked("get health check supported packages")) { + return; + } + + Slog.d(TAG, "Getting health check supported packages"); + try { + mRemoteService.getSupportedPackages(new RemoteCallback(result -> { + List<PackageConfig> packages = + result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES, android.service.watchdog.ExplicitHealthCheckService.PackageConfig.class); + Slog.i(TAG, "Explicit health check supported packages " + packages); + consumer.accept(packages); + })); + } catch (RemoteException e) { + // Request failed, treat as if all observed packages are supported, if any packages + // expire during this period, we may incorrectly treat it as failing health checks + // even if we don't support health checks for the package. + Slog.w(TAG, "Failed to get health check supported packages", e); + } + } + } + + /** + * Returns the packages for which health checks are currently in progress. + * The packages will be returned to the {@code consumer}. + */ + private void getRequestedPackages(Consumer<List<String>> consumer) { + synchronized (mLock) { + if (!prepareServiceLocked("get health check requested packages")) { + return; + } + + Slog.d(TAG, "Getting health check requested packages"); + try { + mRemoteService.getRequestedPackages(new RemoteCallback(result -> { + List<String> packages = result.getStringArrayList(EXTRA_REQUESTED_PACKAGES); + Slog.i(TAG, "Explicit health check requested packages " + packages); + consumer.accept(packages); + })); + } catch (RemoteException e) { + // Request failed, treat as if we haven't requested any packages, if any packages + // were actually requested, they will not be cancelled now. May be cancelled later + Slog.w(TAG, "Failed to get health check requested packages", e); + } + } + } + + /** + * Binds to the explicit health check service if the controller is enabled and + * not already bound. + */ + private void bindService() { + synchronized (mLock) { + if (!mEnabled || mConnection != null || mRemoteService != null) { + if (!mEnabled) { + Slog.i(TAG, "Not binding to service, service disabled"); + } else if (mRemoteService != null) { + Slog.i(TAG, "Not binding to service, service already connected"); + } else { + Slog.i(TAG, "Not binding to service, service already connecting"); + } + return; + } + ComponentName component = getServiceComponentNameLocked(); + if (component == null) { + Slog.wtf(TAG, "Explicit health check service not found"); + return; + } + + Intent intent = new Intent(); + intent.setComponent(component); + mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Slog.i(TAG, "Explicit health check service is connected " + name); + initState(service); + } + + @Override + @MainThread + public void onServiceDisconnected(ComponentName name) { + // Service crashed or process was killed, #onServiceConnected will be called. + // Don't need to re-bind. + Slog.i(TAG, "Explicit health check service is disconnected " + name); + synchronized (mLock) { + mRemoteService = null; + } + } + + @Override + public void onBindingDied(ComponentName name) { + // Application hosting service probably got updated + // Need to re-bind. + Slog.i(TAG, "Explicit health check service binding is dead. Rebind: " + name); + unbindService(); + bindService(); + } + + @Override + public void onNullBinding(ComponentName name) { + // Should never happen. Service returned null from #onBind. + Slog.wtf(TAG, "Explicit health check service binding is null?? " + name); + } + }; + + mContext.bindServiceAsUser(intent, mConnection, + Context.BIND_AUTO_CREATE, UserHandle.SYSTEM); + Slog.i(TAG, "Explicit health check service is bound"); + } + } + + /** Unbinds the explicit health check service. */ + private void unbindService() { + synchronized (mLock) { + if (mRemoteService != null) { + mContext.unbindService(mConnection); + mRemoteService = null; + mConnection = null; + } + Slog.i(TAG, "Explicit health check service is unbound"); + } + } + + @GuardedBy("mLock") + @Nullable + private ServiceInfo getServiceInfoLocked() { + final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE); + final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA + | PackageManager.MATCH_SYSTEM_ONLY); + if (resolveInfo == null || resolveInfo.serviceInfo == null) { + Slog.w(TAG, "No valid components found."); + return null; + } + return resolveInfo.serviceInfo; + } + + @GuardedBy("mLock") + @Nullable + private ComponentName getServiceComponentNameLocked() { + final ServiceInfo serviceInfo = getServiceInfoLocked(); + if (serviceInfo == null) { + return null; + } + + final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); + if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE + .equals(serviceInfo.permission)) { + Slog.w(TAG, name.flattenToShortString() + " does not require permission " + + Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE); + return null; + } + return name; + } + + private void initState(IBinder service) { + synchronized (mLock) { + if (!mEnabled) { + Slog.w(TAG, "Attempting to connect disabled service?? Unbinding..."); + // Very unlikely, but we disabled the service after binding but before we connected + unbindService(); + return; + } + mRemoteService = IExplicitHealthCheckService.Stub.asInterface(service); + try { + mRemoteService.setCallback(new RemoteCallback(result -> { + String packageName = result.getString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE); + if (!TextUtils.isEmpty(packageName)) { + if (mPassedConsumer == null) { + Slog.wtf(TAG, "Health check passed for package " + packageName + + "but no consumer registered."); + } else { + // Call without lock held + mPassedConsumer.accept(packageName); + } + } else { + Slog.wtf(TAG, "Empty package passed explicit health check?"); + } + })); + Slog.i(TAG, "Service initialized, syncing requests"); + } catch (RemoteException e) { + Slog.wtf(TAG, "Could not setCallback on explicit health check service"); + } + } + // Calling outside lock + mNotifySyncRunnable.run(); + } + + /** + * Prepares the health check service to receive requests. + * + * @return {@code true} if it is ready and we can proceed with a request, + * {@code false} otherwise. If it is not ready, and the service is enabled, + * we will bind and the request should be automatically attempted later. + */ + @GuardedBy("mLock") + private boolean prepareServiceLocked(String action) { + if (mRemoteService != null && mEnabled) { + return true; + } + Slog.i(TAG, "Service not ready to " + action + + (mEnabled ? ". Binding..." : ". Disabled")); + if (mEnabled) { + bindService(); + } + return false; + } +} diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java new file mode 100644 index 000000000000..9a8261c20f8a --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -0,0 +1,2094 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.content.Intent.ACTION_REBOOT; +import static android.content.Intent.ACTION_SHUTDOWN; +import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig; + +import static com.android.server.crashrecovery.CrashRecoveryUtils.dumpCrashRecoveryEvents; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.VersionedPackage; +import android.crashrecovery.flags.Flags; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Process; +import android.os.SystemProperties; +import android.provider.DeviceConfig; +import android.sysprop.CrashRecoveryProperties; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.AtomicFile; +import android.util.EventLog; +import android.util.IndentingPrintWriter; +import android.util.LongArrayQueue; +import android.util.Slog; +import android.util.Xml; +import android.util.XmlUtils; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.utils.BackgroundThread; +import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Monitors the health of packages on the system and notifies interested observers when packages + * fail. On failure, the registered observer with the least user impacting mitigation will + * be notified. + * @hide + */ +public class PackageWatchdog { + private static final String TAG = "PackageWatchdog"; + + static final String PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS = + "watchdog_trigger_failure_duration_millis"; + static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT = + "watchdog_trigger_failure_count"; + static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = + "watchdog_explicit_health_check_enabled"; + + // TODO: make the following values configurable via DeviceConfig + private static final long NATIVE_CRASH_POLLING_INTERVAL_MILLIS = + TimeUnit.SECONDS.toMillis(30); + private static final long NUMBER_OF_NATIVE_CRASH_POLLS = 10; + + + /** Reason for package failure could not be determined. */ + public static final int FAILURE_REASON_UNKNOWN = 0; + + /** The package had a native crash. */ + public static final int FAILURE_REASON_NATIVE_CRASH = 1; + + /** The package failed an explicit health check. */ + public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; + + /** The app crashed. */ + public static final int FAILURE_REASON_APP_CRASH = 3; + + /** The app was not responding. */ + public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; + + /** The device was boot looping. */ + public static final int FAILURE_REASON_BOOT_LOOP = 5; + + /** @hide */ + @IntDef(prefix = { "FAILURE_REASON_" }, value = { + FAILURE_REASON_UNKNOWN, + FAILURE_REASON_NATIVE_CRASH, + FAILURE_REASON_EXPLICIT_HEALTH_CHECK, + FAILURE_REASON_APP_CRASH, + FAILURE_REASON_APP_NOT_RESPONDING, + FAILURE_REASON_BOOT_LOOP + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FailureReasons {} + + // Duration to count package failures before it resets to 0 + @VisibleForTesting + static final int DEFAULT_TRIGGER_FAILURE_DURATION_MS = + (int) TimeUnit.MINUTES.toMillis(1); + // Number of package failures within the duration above before we notify observers + @VisibleForTesting + static final int DEFAULT_TRIGGER_FAILURE_COUNT = 5; + @VisibleForTesting + static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); + // Sliding window for tracking how many mitigation calls were made for a package. + @VisibleForTesting + static final long DEFAULT_DEESCALATION_WINDOW_MS = TimeUnit.HOURS.toMillis(1); + // Whether explicit health checks are enabled or not + private static final boolean DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED = true; + + @VisibleForTesting + static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5; + + static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); + + // Time needed to apply mitigation + private static final String MITIGATION_WINDOW_MS = + "persist.device_config.configuration.mitigation_window_ms"; + @VisibleForTesting + static final long DEFAULT_MITIGATION_WINDOW_MS = TimeUnit.SECONDS.toMillis(5); + + // Threshold level at which or above user might experience significant disruption. + private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD = + "persist.device_config.configuration.major_user_impact_level_threshold"; + private static final int DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_71; + + // Comma separated list of all packages exempt from user impact level threshold. If a package + // in the list is crash looping, all the mitigations including factory reset will be performed. + private static final String PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD = + "persist.device_config.configuration.packages_exempt_from_impact_level_threshold"; + + // Comma separated list of default packages exempt from user impact level threshold. + private static final String DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD = + "com.android.systemui"; + + private long mNumberOfNativeCrashPollsRemaining; + + private static final int DB_VERSION = 1; + private static final String TAG_PACKAGE_WATCHDOG = "package-watchdog"; + private static final String TAG_PACKAGE = "package"; + private static final String TAG_OBSERVER = "observer"; + private static final String ATTR_VERSION = "version"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_DURATION = "duration"; + private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration"; + private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check"; + private static final String ATTR_MITIGATION_CALLS = "mitigation-calls"; + private static final String ATTR_MITIGATION_COUNT = "mitigation-count"; + + // A file containing information about the current mitigation count in the case of a boot loop. + // This allows boot loop information to persist in the case of an fs-checkpoint being + // aborted. + private static final String METADATA_FILE = "/metadata/watchdog/mitigation_count.txt"; + + /** + * EventLog tags used when logging into the event log. Note the values must be sync with + * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct + * name translation. + */ + private static final int LOG_TAG_RESCUE_NOTE = 2900; + + private static final Object sPackageWatchdogLock = new Object(); + @GuardedBy("sPackageWatchdogLock") + private static PackageWatchdog sPackageWatchdog; + + private final Object mLock = new Object(); + // System server context + private final Context mContext; + // Handler to run short running tasks + private final Handler mShortTaskHandler; + // Handler for processing IO and long running tasks + private final Handler mLongTaskHandler; + // Contains (observer-name -> observer-handle) that have ever been registered from + // previous boots. Observers with all packages expired are periodically pruned. + // It is saved to disk on system shutdown and repouplated on startup so it survives reboots. + @GuardedBy("mLock") + private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>(); + // File containing the XML data of monitored packages /data/system/package-watchdog.xml + private final AtomicFile mPolicyFile; + private final ExplicitHealthCheckController mHealthCheckController; + private final Runnable mSyncRequests = this::syncRequests; + private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason; + private final Runnable mSaveToFile = this::saveToFile; + private final SystemClock mSystemClock; + private final BootThreshold mBootThreshold; + private final DeviceConfig.OnPropertiesChangedListener + mOnPropertyChangedListener = this::onPropertyChanged; + + private final Set<String> mPackagesExemptFromImpactLevelThreshold = new ArraySet<>(); + + // The set of packages that have been synced with the ExplicitHealthCheckController + @GuardedBy("mLock") + private Set<String> mRequestedHealthCheckPackages = new ArraySet<>(); + @GuardedBy("mLock") + private boolean mIsPackagesReady; + // Flag to control whether explicit health checks are supported or not + @GuardedBy("mLock") + private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED; + @GuardedBy("mLock") + private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; + @GuardedBy("mLock") + private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; + // SystemClock#uptimeMillis when we last executed #syncState + // 0 if no prune is scheduled. + @GuardedBy("mLock") + private long mUptimeAtLastStateSync; + // If true, sync explicit health check packages with the ExplicitHealthCheckController. + @GuardedBy("mLock") + private boolean mSyncRequired = false; + + @GuardedBy("mLock") + private long mLastMitigation = -1000000; + + @FunctionalInterface + @VisibleForTesting + interface SystemClock { + long uptimeMillis(); + } + + private PackageWatchdog(Context context) { + // Needs to be constructed inline + this(context, new AtomicFile( + new File(new File(Environment.getDataDirectory(), "system"), + "package-watchdog.xml")), + new Handler(Looper.myLooper()), BackgroundThread.getHandler(), + new ExplicitHealthCheckController(context), + android.os.SystemClock::uptimeMillis); + } + + /** + * Creates a PackageWatchdog that allows injecting dependencies. + */ + @VisibleForTesting + PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler, + Handler longTaskHandler, ExplicitHealthCheckController controller, + SystemClock clock) { + mContext = context; + mPolicyFile = policyFile; + mShortTaskHandler = shortTaskHandler; + mLongTaskHandler = longTaskHandler; + mHealthCheckController = controller; + mSystemClock = clock; + mNumberOfNativeCrashPollsRemaining = NUMBER_OF_NATIVE_CRASH_POLLS; + mBootThreshold = new BootThreshold(DEFAULT_BOOT_LOOP_TRIGGER_COUNT, + DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS); + + loadFromFile(); + sPackageWatchdog = this; + } + + /** Creates or gets singleton instance of PackageWatchdog. */ + public static @NonNull PackageWatchdog getInstance(@NonNull Context context) { + synchronized (sPackageWatchdogLock) { + if (sPackageWatchdog == null) { + new PackageWatchdog(context); + } + return sPackageWatchdog; + } + } + + /** + * Called during boot to notify when packages are ready on the device so we can start + * binding. + * @hide + */ + public void onPackagesReady() { + synchronized (mLock) { + mIsPackagesReady = true; + mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), + packages -> onSupportedPackages(packages), + this::onSyncRequestNotified); + setPropertyChangedListenerLocked(); + updateConfigs(); + } + } + + /** + * Registers {@code observer} to listen for package failures. Add a new ObserverInternal for + * this observer if it does not already exist. + * + * <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 + */ + public void registerHealthObserver(PackageHealthObserver observer) { + synchronized (mLock) { + ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier()); + if (internalObserver != null) { + internalObserver.registeredObserver = observer; + } else { + internalObserver = new ObserverInternal(observer.getUniqueIdentifier(), + new ArrayList<>()); + internalObserver.registeredObserver = observer; + mAllObservers.put(observer.getUniqueIdentifier(), internalObserver); + syncState("added new observer"); + } + } + } + + /** + * Starts observing the health of the {@code packages} for {@code observer} and notifies + * {@code observer} of any package failures within the monitoring duration. + * + * <p>If monitoring a package supporting explicit health check, at the end of the monitoring + * duration if {@link #onHealthCheckPassed} was never called, + * {@link PackageHealthObserver#execute} 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}. + * + * <p>If {@code packageNames} is empty, this will be a no-op. + * + * <p>If {@code durationMs} is less than 1, a default monitoring duration + * {@link #DEFAULT_OBSERVING_DURATION_MS} will be used. + * @hide + */ + public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames, + long durationMs) { + 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 " + + observer.getUniqueIdentifier() + ". Not observing packages " + packageNames); + durationMs = 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); + if (pkg != null) { + packages.add(pkg); + } else { + Slog.w(TAG, "Failed to create MonitoredPackage for pkg=" + packageNames.get(i)); + } + } + + if (packages.isEmpty()) { + return; + } + + // Sync before we add the new packages to the observers. This will #pruneObservers, + // causing any elapsed time to be deducted from all existing packages before we add new + // packages. This maintains the invariant that the elapsed time for ALL (new and existing) + // packages is the same. + mLongTaskHandler.post(() -> { + syncState("observing new packages"); + + synchronized (mLock) { + ObserverInternal oldObserver = mAllObservers.get(observer.getUniqueIdentifier()); + if (oldObserver == null) { + Slog.d(TAG, observer.getUniqueIdentifier() + " started monitoring health " + + "of packages " + packageNames); + mAllObservers.put(observer.getUniqueIdentifier(), + new ObserverInternal(observer.getUniqueIdentifier(), packages)); + } else { + Slog.d(TAG, observer.getUniqueIdentifier() + " added the following " + + "packages to monitor " + packageNames); + oldObserver.updatePackagesLocked(packages); + } + } + + // 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"); + }); + + } + + /** + * 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) { + mLongTaskHandler.post(() -> { + synchronized (mLock) { + mAllObservers.remove(observer.getUniqueIdentifier()); + } + syncState("unregistering observer: " + observer.getUniqueIdentifier()); + }); + } + + /** + * Called when a process fails due to a crash, ANR or explicit health check. + * + * <p>For each package contained in the process, one registered observer with the least user + * impact will be notified for mitigation. + * + * <p>This method could be called frequently if there is a severe problem on the device. + */ + public void onPackageFailure(@NonNull List<VersionedPackage> packages, + @FailureReasons int failureReason) { + if (packages == null) { + Slog.w(TAG, "Could not resolve a list of failing packages"); + return; + } + synchronized (mLock) { + final long now = mSystemClock.uptimeMillis(); + if (Flags.recoverabilityDetection()) { + if (now >= mLastMitigation + && (now - mLastMitigation) < getMitigationWindowMs()) { + Slog.i(TAG, "Skipping onPackageFailure mitigation"); + return; + } + } + } + mLongTaskHandler.post(() -> { + synchronized (mLock) { + if (mAllObservers.isEmpty()) { + return; + } + boolean requiresImmediateAction = (failureReason == FAILURE_REASON_NATIVE_CRASH + || failureReason == FAILURE_REASON_EXPLICIT_HEALTH_CHECK); + if (requiresImmediateAction) { + handleFailureImmediately(packages, failureReason); + } else { + for (int pIndex = 0; pIndex < packages.size(); pIndex++) { + VersionedPackage versionedPackage = packages.get(pIndex); + // Observer that will receive failure for versionedPackage + PackageHealthObserver currentObserverToNotify = null; + int currentObserverImpact = Integer.MAX_VALUE; + MonitoredPackage currentMonitoredPackage = null; + + // Find observer with least user impact + for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { + ObserverInternal observer = mAllObservers.valueAt(oIndex); + PackageHealthObserver registeredObserver = observer.registeredObserver; + if (registeredObserver != null + && observer.onPackageFailureLocked( + versionedPackage.getPackageName())) { + MonitoredPackage p = observer.getMonitoredPackage( + versionedPackage.getPackageName()); + int mitigationCount = 1; + if (p != null) { + mitigationCount = p.getMitigationCountLocked() + 1; + } + int impact = registeredObserver.onHealthCheckFailed( + versionedPackage, failureReason, mitigationCount); + if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 + && impact < currentObserverImpact) { + currentObserverToNotify = registeredObserver; + currentObserverImpact = impact; + currentMonitoredPackage = p; + } + } + } + + // Execute action with least user impact + if (currentObserverToNotify != null) { + int mitigationCount = 1; + if (currentMonitoredPackage != null) { + currentMonitoredPackage.noteMitigationCallLocked(); + mitigationCount = + currentMonitoredPackage.getMitigationCountLocked(); + } + if (Flags.recoverabilityDetection()) { + maybeExecute(currentObserverToNotify, versionedPackage, + failureReason, currentObserverImpact, mitigationCount); + } else { + currentObserverToNotify.execute(versionedPackage, + failureReason, mitigationCount); + } + } + } + } + } + }); + } + + /** + * For native crashes or explicit health check failures, call directly into each observer to + * mitigate the error without going through failure threshold logic. + */ + private void handleFailureImmediately(List<VersionedPackage> packages, + @FailureReasons int failureReason) { + VersionedPackage failingPackage = packages.size() > 0 ? packages.get(0) : null; + PackageHealthObserver currentObserverToNotify = null; + int currentObserverImpact = Integer.MAX_VALUE; + for (ObserverInternal observer: mAllObservers.values()) { + PackageHealthObserver registeredObserver = observer.registeredObserver; + if (registeredObserver != null) { + int impact = registeredObserver.onHealthCheckFailed( + failingPackage, failureReason, 1); + if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 + && impact < currentObserverImpact) { + currentObserverToNotify = registeredObserver; + currentObserverImpact = impact; + } + } + } + if (currentObserverToNotify != null) { + if (Flags.recoverabilityDetection()) { + maybeExecute(currentObserverToNotify, failingPackage, failureReason, + currentObserverImpact, /*mitigationCount=*/ 1); + } else { + currentObserverToNotify.execute(failingPackage, failureReason, 1); + } + } + } + + private void maybeExecute(PackageHealthObserver currentObserverToNotify, + VersionedPackage versionedPackage, + @FailureReasons int failureReason, + int currentObserverImpact, + int mitigationCount) { + if (allowMitigations(currentObserverImpact, versionedPackage)) { + synchronized (mLock) { + mLastMitigation = mSystemClock.uptimeMillis(); + } + currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount); + } + } + + private boolean allowMitigations(int currentObserverImpact, + VersionedPackage versionedPackage) { + return currentObserverImpact < getUserImpactLevelLimit() + || getPackagesExemptFromImpactLevelThreshold().contains( + versionedPackage.getPackageName()); + } + + private long getMitigationWindowMs() { + return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS); + } + + + /** + * Called when the system server boots. If the system server is detected to be in a boot loop, + * query each observer and perform the mitigation action with the lowest user impact. + * + * Note: PackageWatchdog considers system_server restart loop as bootloop. Full reboots + * are not counted in bootloop. + * @hide + */ + @SuppressWarnings("GuardedBy") + public void noteBoot() { + synchronized (mLock) { + // if boot count has reached threshold, start mitigation. + // We wait until threshold number of restarts only for the first time. Perform + // mitigations for every restart after that. + boolean mitigate = mBootThreshold.incrementAndTest(); + if (mitigate) { + if (!Flags.recoverabilityDetection()) { + mBootThreshold.reset(); + } + int mitigationCount = mBootThreshold.getMitigationCount() + 1; + PackageHealthObserver currentObserverToNotify = null; + ObserverInternal currentObserverInternal = null; + int currentObserverImpact = Integer.MAX_VALUE; + for (int i = 0; i < mAllObservers.size(); i++) { + final ObserverInternal observer = mAllObservers.valueAt(i); + PackageHealthObserver registeredObserver = observer.registeredObserver; + if (registeredObserver != null) { + int impact = Flags.recoverabilityDetection() + ? registeredObserver.onBootLoop( + observer.getBootMitigationCount() + 1) + : registeredObserver.onBootLoop(mitigationCount); + if (impact != PackageHealthObserverImpact.USER_IMPACT_LEVEL_0 + && impact < currentObserverImpact) { + currentObserverToNotify = registeredObserver; + currentObserverInternal = observer; + currentObserverImpact = impact; + } + } + } + if (currentObserverToNotify != null) { + if (Flags.recoverabilityDetection()) { + int currentObserverMitigationCount = + currentObserverInternal.getBootMitigationCount() + 1; + currentObserverInternal.setBootMitigationCount( + currentObserverMitigationCount); + saveAllObserversBootMitigationCountToMetadata(METADATA_FILE); + currentObserverToNotify.executeBootLoopMitigation( + currentObserverMitigationCount); + } else { + mBootThreshold.setMitigationCount(mitigationCount); + mBootThreshold.saveMitigationCountToMetadata(); + currentObserverToNotify.executeBootLoopMitigation(mitigationCount); + } + } + } + } + } + + // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also + // avoid holding lock? + // This currently adds about 7ms extra to shutdown thread + /** @hide Writes the package information to file during shutdown. */ + public void writeNow() { + synchronized (mLock) { + // Must only run synchronous tasks as this runs on the ShutdownThread and no other + // thread is guaranteed to run during shutdown. + if (!mAllObservers.isEmpty()) { + mLongTaskHandler.removeCallbacks(mSaveToFile); + pruneObserversLocked(); + saveToFile(); + Slog.i(TAG, "Last write to update package durations"); + } + } + } + + /** + * Enables or disables explicit health checks. + * <p> If explicit health checks are enabled, the health check service is started. + * <p> If explicit health checks are disabled, pending explicit health check requests are + * passed and the health check service is stopped. + */ + private void setExplicitHealthCheckEnabled(boolean enabled) { + synchronized (mLock) { + mIsHealthCheckEnabled = enabled; + mHealthCheckController.setEnabled(enabled); + mSyncRequired = true; + // Prune to update internal state whenever health check is enabled/disabled + syncState("health check state " + (enabled ? "enabled" : "disabled")); + } + } + + /** + * This method should be only called on mShortTaskHandler, since it modifies + * {@link #mNumberOfNativeCrashPollsRemaining}. + */ + private void checkAndMitigateNativeCrashes() { + mNumberOfNativeCrashPollsRemaining--; + // Check if native watchdog reported a crash + if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { + // We rollback all available low impact rollbacks when crash is unattributable + onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); + // we stop polling after an attempt to execute rollback, regardless of whether the + // attempt succeeds or not + } else { + if (mNumberOfNativeCrashPollsRemaining > 0) { + mShortTaskHandler.postDelayed(() -> checkAndMitigateNativeCrashes(), + NATIVE_CRASH_POLLING_INTERVAL_MILLIS); + } + } + } + + /** + * Since this method can eventually trigger a rollback, it should be called + * only once boot has completed {@code onBootCompleted} and not earlier, because the install + * session must be entirely completed before we try to rollback. + * @hide + */ + public void scheduleCheckAndMitigateNativeCrashes() { + Slog.i(TAG, "Scheduling " + mNumberOfNativeCrashPollsRemaining + " polls to check " + + "and mitigate native crashes"); + mShortTaskHandler.post(()->checkAndMitigateNativeCrashes()); + } + + private int getUserImpactLevelLimit() { + return SystemProperties.getInt(MAJOR_USER_IMPACT_LEVEL_THRESHOLD, + DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD); + } + + private Set<String> getPackagesExemptFromImpactLevelThreshold() { + if (mPackagesExemptFromImpactLevelThreshold.isEmpty()) { + String packageNames = SystemProperties.get(PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD, + DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD); + return Set.of(packageNames.split("\\s*,\\s*")); + } + return mPackagesExemptFromImpactLevelThreshold; + } + + /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. + * @hide + */ + @Retention(SOURCE) + @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_10, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_40, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_71, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_75, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_80, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_90, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_100}) + public @interface PackageHealthObserverImpact { + /** No action to take. */ + int USER_IMPACT_LEVEL_0 = 0; + /* Action has low user impact, user of a device will barely notice. */ + int USER_IMPACT_LEVEL_10 = 10; + /* Actions having medium user impact, user of a device will likely notice. */ + int USER_IMPACT_LEVEL_20 = 20; + int USER_IMPACT_LEVEL_30 = 30; + int USER_IMPACT_LEVEL_40 = 40; + int USER_IMPACT_LEVEL_50 = 50; + int USER_IMPACT_LEVEL_70 = 70; + /* Action has high user impact, a last resort, user of a device will be very frustrated. */ + int USER_IMPACT_LEVEL_71 = 71; + int USER_IMPACT_LEVEL_75 = 75; + int USER_IMPACT_LEVEL_80 = 80; + int USER_IMPACT_LEVEL_90 = 90; + int USER_IMPACT_LEVEL_100 = 100; + } + + /** Register instances of this interface to receive notifications on package failure. */ + public interface PackageHealthObserver { + /** + * Called when health check fails for the {@code versionedPackage}. + * + * @param versionedPackage the package that is failing. This may be null if a native + * service is crashing. + * @param failureReason the type of failure that is occurring. + * @param mitigationCount the number of times mitigation has been called for this package + * (including this time). + * + * + * @return any one of {@link PackageHealthObserverImpact} to express the impact + * to the user on {@link #execute} + */ + @PackageHealthObserverImpact int onHealthCheckFailed( + @Nullable VersionedPackage versionedPackage, + @FailureReasons int failureReason, + int mitigationCount); + + /** + * Executes mitigation for {@link #onHealthCheckFailed}. + * + * @param versionedPackage the package that is failing. This may be null if a native + * service is crashing. + * @param failureReason the type of failure that is occurring. + * @param mitigationCount the number of times mitigation has been called for this package + * (including this time). + * @return {@code true} if action was executed successfully, {@code false} otherwise + */ + boolean execute(@Nullable VersionedPackage versionedPackage, + @FailureReasons int failureReason, int mitigationCount); + + + /** + * Called when the system server has booted several times within a window of time, defined + * by {@link #mBootThreshold} + * + * @param mitigationCount the number of times mitigation has been attempted for this + * boot loop (including this time). + */ + default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + + /** + * Executes mitigation for {@link #onBootLoop} + * @param mitigationCount the number of times mitigation has been attempted for this + * boot loop (including this time). + */ + default boolean executeBootLoopMitigation(int mitigationCount) { + return false; + } + + // TODO(b/120598832): Ensure uniqueness? + /** + * Identifier for the observer, should not change across device updates otherwise the + * watchdog may drop observing packages with the old name. + */ + @NonNull String getUniqueIdentifier(); + + /** + * An observer will not be pruned if this is set, even if the observer is not explicitly + * monitoring any packages. + */ + default boolean isPersistent() { + return false; + } + + /** + * Returns {@code true} if this observer wishes to observe the given package, {@code false} + * 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}. + */ + default boolean mayObservePackage(@NonNull String packageName) { + return false; + } + } + + @VisibleForTesting + long getTriggerFailureCount() { + synchronized (mLock) { + return mTriggerFailureCount; + } + } + + @VisibleForTesting + long getTriggerFailureDurationMs() { + synchronized (mLock) { + return mTriggerFailureDurationMs; + } + } + + /** + * Serializes and syncs health check requests with the {@link ExplicitHealthCheckController}. + */ + private void syncRequestsAsync() { + mShortTaskHandler.removeCallbacks(mSyncRequests); + mShortTaskHandler.post(mSyncRequests); + } + + /** + * Syncs health check requests with the {@link ExplicitHealthCheckController}. + * Calls to this must be serialized. + * + * @see #syncRequestsAsync + */ + private void syncRequests() { + boolean syncRequired = false; + synchronized (mLock) { + if (mIsPackagesReady) { + Set<String> packages = getPackagesPendingHealthChecksLocked(); + if (mSyncRequired || !packages.equals(mRequestedHealthCheckPackages) + || packages.isEmpty()) { + syncRequired = true; + mRequestedHealthCheckPackages = packages; + } + } // else, we will sync requests when packages become ready + } + + // Call outside lock to avoid holding lock when calling into the controller. + if (syncRequired) { + Slog.i(TAG, "Syncing health check requests for packages: " + + mRequestedHealthCheckPackages); + mHealthCheckController.syncRequests(mRequestedHealthCheckPackages); + mSyncRequired = false; + } + } + + /** + * Updates the observers monitoring {@code packageName} that explicit health check has passed. + * + * <p> This update is strictly for registered observers at the time of the call + * Observers that register after this signal will have no knowledge of prior signals and will + * effectively behave as if the explicit health check hasn't passed for {@code packageName}. + * + * <p> {@code packageName} can still be considered failed if reported by + * {@link #onPackageFailureLocked} before the package expires. + * + * <p> Triggered by components outside the system server when they are fully functional after an + * update. + */ + private void onHealthCheckPassed(String packageName) { + Slog.i(TAG, "Health check passed for package: " + packageName); + boolean isStateChanged = false; + + synchronized (mLock) { + for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) { + ObserverInternal observer = mAllObservers.valueAt(observerIdx); + MonitoredPackage monitoredPackage = observer.getMonitoredPackage(packageName); + + if (monitoredPackage != null) { + int oldState = monitoredPackage.getHealthCheckStateLocked(); + int newState = monitoredPackage.tryPassHealthCheckLocked(); + isStateChanged |= oldState != newState; + } + } + } + + if (isStateChanged) { + syncState("health check passed for " + packageName); + } + } + + private void onSupportedPackages(List<PackageConfig> supportedPackages) { + boolean isStateChanged = false; + + Map<String, Long> supportedPackageTimeouts = new ArrayMap<>(); + Iterator<PackageConfig> it = supportedPackages.iterator(); + while (it.hasNext()) { + PackageConfig info = it.next(); + supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis()); + } + + synchronized (mLock) { + Slog.d(TAG, "Received supported packages " + supportedPackages); + Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); + while (oit.hasNext()) { + Iterator<MonitoredPackage> pit = oit.next().getMonitoredPackages() + .values().iterator(); + while (pit.hasNext()) { + MonitoredPackage monitoredPackage = pit.next(); + String packageName = monitoredPackage.getName(); + int oldState = monitoredPackage.getHealthCheckStateLocked(); + int newState; + + if (supportedPackageTimeouts.containsKey(packageName)) { + // Supported packages become ACTIVE if currently INACTIVE + newState = monitoredPackage.setHealthCheckActiveLocked( + supportedPackageTimeouts.get(packageName)); + } else { + // Unsupported packages are marked as PASSED unless already FAILED + newState = monitoredPackage.tryPassHealthCheckLocked(); + } + isStateChanged |= oldState != newState; + } + } + } + + if (isStateChanged) { + syncState("updated health check supported packages " + supportedPackages); + } + } + + private void onSyncRequestNotified() { + synchronized (mLock) { + mSyncRequired = true; + syncRequestsAsync(); + } + } + + @GuardedBy("mLock") + private Set<String> getPackagesPendingHealthChecksLocked() { + Set<String> packages = new ArraySet<>(); + Iterator<ObserverInternal> oit = mAllObservers.values().iterator(); + while (oit.hasNext()) { + ObserverInternal observer = oit.next(); + Iterator<MonitoredPackage> pit = + observer.getMonitoredPackages().values().iterator(); + while (pit.hasNext()) { + MonitoredPackage monitoredPackage = pit.next(); + String packageName = monitoredPackage.getName(); + if (monitoredPackage.isPendingHealthChecksLocked()) { + packages.add(packageName); + } + } + } + return packages; + } + + /** + * Syncs the state of the observers. + * + * <p> Prunes all observers, saves new state to disk, syncs health check requests with the + * health check service and schedules the next state sync. + */ + private void syncState(String reason) { + synchronized (mLock) { + Slog.i(TAG, "Syncing state, reason: " + reason); + pruneObserversLocked(); + + saveToFileAsync(); + syncRequestsAsync(); + + // Done syncing state, schedule the next state sync + scheduleNextSyncStateLocked(); + } + } + + private void syncStateWithScheduledReason() { + syncState("scheduled"); + } + + @GuardedBy("mLock") + private void scheduleNextSyncStateLocked() { + long durationMs = getNextStateSyncMillisLocked(); + mShortTaskHandler.removeCallbacks(mSyncStateWithScheduledReason); + if (durationMs == Long.MAX_VALUE) { + Slog.i(TAG, "Cancelling state sync, nothing to sync"); + mUptimeAtLastStateSync = 0; + } else { + mUptimeAtLastStateSync = mSystemClock.uptimeMillis(); + mShortTaskHandler.postDelayed(mSyncStateWithScheduledReason, durationMs); + } + } + + /** + * Returns the next duration in millis to sync the watchdog state. + * + * @returns Long#MAX_VALUE if there are no observed packages. + */ + @GuardedBy("mLock") + private long getNextStateSyncMillisLocked() { + long shortestDurationMs = Long.MAX_VALUE; + for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { + ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex) + .getMonitoredPackages(); + for (int pIndex = 0; pIndex < packages.size(); pIndex++) { + MonitoredPackage mp = packages.valueAt(pIndex); + long duration = mp.getShortestScheduleDurationMsLocked(); + if (duration < shortestDurationMs) { + shortestDurationMs = duration; + } + } + } + return shortestDurationMs; + } + + /** + * Removes {@code elapsedMs} milliseconds from all durations on monitored packages + * and updates other internal state. + */ + @GuardedBy("mLock") + private void pruneObserversLocked() { + long elapsedMs = mUptimeAtLastStateSync == 0 + ? 0 : mSystemClock.uptimeMillis() - mUptimeAtLastStateSync; + if (elapsedMs <= 0) { + Slog.i(TAG, "Not pruning observers, elapsed time: " + elapsedMs + "ms"); + return; + } + + Iterator<ObserverInternal> it = mAllObservers.values().iterator(); + while (it.hasNext()) { + ObserverInternal observer = it.next(); + Set<MonitoredPackage> failedPackages = + observer.prunePackagesLocked(elapsedMs); + if (!failedPackages.isEmpty()) { + onHealthCheckFailed(observer, failedPackages); + } + if (observer.getMonitoredPackages().isEmpty() && (observer.registeredObserver == null + || !observer.registeredObserver.isPersistent())) { + Slog.i(TAG, "Discarding observer " + observer.name + ". All packages expired"); + it.remove(); + } + } + } + + private void onHealthCheckFailed(ObserverInternal observer, + Set<MonitoredPackage> failedPackages) { + mLongTaskHandler.post(() -> { + synchronized (mLock) { + PackageHealthObserver registeredObserver = observer.registeredObserver; + if (registeredObserver != null) { + Iterator<MonitoredPackage> it = failedPackages.iterator(); + while (it.hasNext()) { + VersionedPackage versionedPkg = getVersionedPackage(it.next().getName()); + if (versionedPkg != null) { + Slog.i(TAG, + "Explicit health check failed for package " + versionedPkg); + registeredObserver.execute(versionedPkg, + PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK, 1); + } + } + } + } + }); + } + + /** + * Gets PackageInfo for the given package. Matches any user and apex. + * + * @throws PackageManager.NameNotFoundException if no such package is installed. + */ + private PackageInfo getPackageInfo(String packageName) + throws PackageManager.NameNotFoundException { + PackageManager pm = mContext.getPackageManager(); + try { + // The MATCH_ANY_USER flag doesn't mix well with the MATCH_APEX + // flag, so make two separate attempts to get the package info. + // We don't need both flags at the same time because we assume + // apex files are always installed for all users. + return pm.getPackageInfo(packageName, PackageManager.MATCH_ANY_USER); + } catch (PackageManager.NameNotFoundException e) { + return pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); + } + } + + @Nullable + private VersionedPackage getVersionedPackage(String packageName) { + final PackageManager pm = mContext.getPackageManager(); + if (pm == null || TextUtils.isEmpty(packageName)) { + return null; + } + try { + final long versionCode = getPackageInfo(packageName).getLongVersionCode(); + return new VersionedPackage(packageName, versionCode); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + } + + /** + * Loads mAllObservers from file. + * + * <p>Note that this is <b>not</b> thread safe and should only called be called + * from the constructor. + */ + private void loadFromFile() { + InputStream infile = null; + mAllObservers.clear(); + try { + infile = mPolicyFile.openRead(); + final TypedXmlPullParser parser = Xml.resolvePullParser(infile); + XmlUtils.beginDocument(parser, TAG_PACKAGE_WATCHDOG); + int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + ObserverInternal observer = ObserverInternal.read(parser, this); + if (observer != null) { + mAllObservers.put(observer.name, observer); + } + } + } catch (FileNotFoundException e) { + // Nothing to monitor + } catch (IOException | NumberFormatException | XmlPullParserException e) { + Slog.wtf(TAG, "Unable to read monitored packages, deleting file", e); + mPolicyFile.delete(); + } finally { + IoUtils.closeQuietly(infile); + } + } + + private void onPropertyChanged(DeviceConfig.Properties properties) { + try { + updateConfigs(); + } catch (Exception ignore) { + Slog.w(TAG, "Failed to reload device config changes"); + } + } + + /** Adds a {@link DeviceConfig#OnPropertiesChangedListener}. */ + private void setPropertyChangedListenerLocked() { + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_ROLLBACK, + mContext.getMainExecutor(), + mOnPropertyChangedListener); + } + + @VisibleForTesting + void removePropertyChangedListener() { + DeviceConfig.removeOnPropertiesChangedListener(mOnPropertyChangedListener); + } + + /** + * Health check is enabled or disabled after reading the flags + * from DeviceConfig. + */ + @VisibleForTesting + void updateConfigs() { + synchronized (mLock) { + mTriggerFailureCount = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, + DEFAULT_TRIGGER_FAILURE_COUNT); + if (mTriggerFailureCount <= 0) { + mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; + } + + mTriggerFailureDurationMs = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_TRIGGER_DURATION_MILLIS, + DEFAULT_TRIGGER_FAILURE_DURATION_MS); + if (mTriggerFailureDurationMs <= 0) { + mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; + } + + setExplicitHealthCheckEnabled(DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ROLLBACK, + PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, + DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED)); + } + } + + /** + * Persists mAllObservers to file. Threshold information is ignored. + */ + private boolean saveToFile() { + Slog.i(TAG, "Saving observer state to file"); + synchronized (mLock) { + FileOutputStream stream; + try { + stream = mPolicyFile.startWrite(); + } catch (IOException e) { + Slog.w(TAG, "Cannot update monitored packages", e); + return false; + } + + try { + TypedXmlSerializer out = Xml.resolveSerializer(stream); + out.startDocument(null, true); + out.startTag(null, TAG_PACKAGE_WATCHDOG); + out.attributeInt(null, ATTR_VERSION, DB_VERSION); + for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { + mAllObservers.valueAt(oIndex).writeLocked(out); + } + out.endTag(null, TAG_PACKAGE_WATCHDOG); + out.endDocument(); + mPolicyFile.finishWrite(stream); + return true; + } catch (IOException e) { + Slog.w(TAG, "Failed to save monitored packages, restoring backup", e); + mPolicyFile.failWrite(stream); + return false; + } finally { + IoUtils.closeQuietly(stream); + } + } + } + + private void saveToFileAsync() { + if (!mLongTaskHandler.hasCallbacks(mSaveToFile)) { + mLongTaskHandler.post(mSaveToFile); + } + } + + /** @hide Convert a {@code LongArrayQueue} to a String of comma-separated values. */ + public static String longArrayQueueToString(LongArrayQueue queue) { + if (queue.size() > 0) { + StringBuilder sb = new StringBuilder(); + sb.append(queue.get(0)); + for (int i = 1; i < queue.size(); i++) { + sb.append(","); + sb.append(queue.get(i)); + } + return sb.toString(); + } + return ""; + } + + /** @hide Parse a comma-separated String of longs into a LongArrayQueue. */ + public static LongArrayQueue parseLongArrayQueue(String commaSeparatedValues) { + LongArrayQueue result = new LongArrayQueue(); + if (!TextUtils.isEmpty(commaSeparatedValues)) { + String[] values = commaSeparatedValues.split(","); + for (String value : values) { + result.addLast(Long.parseLong(value)); + } + } + return result; + } + + + /** Dump status of every observer in mAllObservers. */ + public void dump(@NonNull PrintWriter pw) { + if (Flags.synchronousRebootInRescueParty() && RescueParty.isRecoveryTriggeredReboot()) { + dumpInternal(pw); + } else { + synchronized (mLock) { + dumpInternal(pw); + } + } + } + + private void dumpInternal(@NonNull PrintWriter pw) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.println("Package Watchdog status"); + ipw.increaseIndent(); + synchronized (mLock) { + for (String observerName : mAllObservers.keySet()) { + ipw.println("Observer name: " + observerName); + ipw.increaseIndent(); + ObserverInternal observerInternal = mAllObservers.get(observerName); + observerInternal.dump(ipw); + ipw.decreaseIndent(); + } + } + ipw.decreaseIndent(); + dumpCrashRecoveryEvents(ipw); + } + + @VisibleForTesting + @GuardedBy("mLock") + void registerObserverInternal(ObserverInternal observerInternal) { + mAllObservers.put(observerInternal.name, observerInternal); + } + + /** + * Represents an observer monitoring a set of packages along with the failure thresholds for + * each package. + * + * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing + * instances of this class. + */ + static class ObserverInternal { + public final String name; + @GuardedBy("mLock") + private final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>(); + @Nullable + @GuardedBy("mLock") + public PackageHealthObserver registeredObserver; + private int mMitigationCount; + + ObserverInternal(String name, List<MonitoredPackage> packages) { + this(name, packages, /*mitigationCount=*/ 0); + } + + ObserverInternal(String name, List<MonitoredPackage> packages, int mitigationCount) { + this.name = name; + updatePackagesLocked(packages); + this.mMitigationCount = mitigationCount; + } + + /** + * Writes important {@link MonitoredPackage} details for this observer to file. + * Does not persist any package failure thresholds. + */ + @GuardedBy("mLock") + public boolean writeLocked(TypedXmlSerializer out) { + try { + out.startTag(null, TAG_OBSERVER); + out.attribute(null, ATTR_NAME, name); + if (Flags.recoverabilityDetection()) { + out.attributeInt(null, ATTR_MITIGATION_COUNT, mMitigationCount); + } + for (int i = 0; i < mPackages.size(); i++) { + MonitoredPackage p = mPackages.valueAt(i); + p.writeLocked(out); + } + out.endTag(null, TAG_OBSERVER); + return true; + } catch (IOException e) { + Slog.w(TAG, "Cannot save observer", e); + return false; + } + } + + public int getBootMitigationCount() { + return mMitigationCount; + } + + public void setBootMitigationCount(int mitigationCount) { + mMitigationCount = mitigationCount; + } + + @GuardedBy("mLock") + public void updatePackagesLocked(List<MonitoredPackage> packages) { + for (int pIndex = 0; pIndex < packages.size(); pIndex++) { + MonitoredPackage p = packages.get(pIndex); + MonitoredPackage existingPackage = getMonitoredPackage(p.getName()); + if (existingPackage != null) { + existingPackage.updateHealthCheckDuration(p.mDurationMs); + } else { + putMonitoredPackage(p); + } + } + } + + /** + * Reduces the monitoring durations of all packages observed by this observer by + * {@code elapsedMs}. If any duration is less than 0, the package is removed from + * observation. If any health check duration is less than 0, the health check result + * is evaluated. + * + * @return a {@link Set} of packages that were removed from the observer without explicit + * health check passing, or an empty list if no package expired for which an explicit health + * check was still pending + */ + @GuardedBy("mLock") + private Set<MonitoredPackage> prunePackagesLocked(long elapsedMs) { + Set<MonitoredPackage> failedPackages = new ArraySet<>(); + Iterator<MonitoredPackage> it = mPackages.values().iterator(); + while (it.hasNext()) { + MonitoredPackage p = it.next(); + int oldState = p.getHealthCheckStateLocked(); + int newState = p.handleElapsedTimeLocked(elapsedMs); + if (oldState != HealthCheckState.FAILED + && newState == HealthCheckState.FAILED) { + Slog.i(TAG, "Package " + p.getName() + " failed health check"); + failedPackages.add(p); + } + if (p.isExpiredLocked()) { + it.remove(); + } + } + return failedPackages; + } + + /** + * Increments failure counts of {@code packageName}. + * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise + * @hide + */ + @GuardedBy("mLock") + public boolean onPackageFailureLocked(String packageName) { + if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent() + && registeredObserver.mayObservePackage(packageName)) { + putMonitoredPackage(sPackageWatchdog.newMonitoredPackage( + packageName, DEFAULT_OBSERVING_DURATION_MS, false)); + } + MonitoredPackage p = getMonitoredPackage(packageName); + if (p != null) { + return p.onFailureLocked(); + } + return false; + } + + /** + * Returns the map of packages monitored by this observer. + * + * @return a mapping of package names to {@link MonitoredPackage} objects. + */ + @GuardedBy("mLock") + public ArrayMap<String, MonitoredPackage> getMonitoredPackages() { + return mPackages; + } + + /** + * Returns the {@link MonitoredPackage} associated with a given package name if the + * package is being monitored by this observer. + * + * @param packageName: the name of the package. + * @return the {@link MonitoredPackage} object associated with the package name if one + * exists, {@code null} otherwise. + */ + @GuardedBy("mLock") + @Nullable + public MonitoredPackage getMonitoredPackage(String packageName) { + return mPackages.get(packageName); + } + + /** + * Associates a {@link MonitoredPackage} with the observer. + * + * @param p: the {@link MonitoredPackage} to store. + */ + @GuardedBy("mLock") + public void putMonitoredPackage(MonitoredPackage p) { + mPackages.put(p.getName(), p); + } + + /** + * Returns one ObserverInternal from the {@code parser} and advances its state. + * + * <p>Note that this method is <b>not</b> thread safe. It should only be called from + * #loadFromFile which in turn is only called on construction of the + * singleton PackageWatchdog. + **/ + public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) { + String observerName = null; + int observerMitigationCount = 0; + if (TAG_OBSERVER.equals(parser.getName())) { + observerName = parser.getAttributeValue(null, ATTR_NAME); + if (TextUtils.isEmpty(observerName)) { + Slog.wtf(TAG, "Unable to read observer name"); + return null; + } + } + List<MonitoredPackage> packages = new ArrayList<>(); + int innerDepth = parser.getDepth(); + try { + if (Flags.recoverabilityDetection()) { + try { + observerMitigationCount = + parser.getAttributeInt(null, ATTR_MITIGATION_COUNT); + } catch (XmlPullParserException e) { + Slog.i( + TAG, + "ObserverInternal mitigation count was not present."); + } + } + while (XmlUtils.nextElementWithin(parser, innerDepth)) { + if (TAG_PACKAGE.equals(parser.getName())) { + try { + MonitoredPackage pkg = watchdog.parseMonitoredPackage(parser); + if (pkg != null) { + packages.add(pkg); + } + } catch (NumberFormatException e) { + Slog.wtf(TAG, "Skipping package for observer " + observerName, e); + continue; + } + } + } + } catch (XmlPullParserException | IOException e) { + Slog.wtf(TAG, "Unable to read observer " + observerName, e); + return null; + } + if (packages.isEmpty()) { + return null; + } + return new ObserverInternal(observerName, packages, observerMitigationCount); + } + + /** Dumps information about this observer and the packages it watches. */ + public void dump(IndentingPrintWriter pw) { + boolean isPersistent = registeredObserver != null && registeredObserver.isPersistent(); + pw.println("Persistent: " + isPersistent); + for (String packageName : mPackages.keySet()) { + MonitoredPackage p = getMonitoredPackage(packageName); + pw.println(packageName + ": "); + pw.increaseIndent(); + pw.println("# Failures: " + p.mFailureHistory.size()); + pw.println("Monitoring duration remaining: " + p.mDurationMs + "ms"); + pw.println("Explicit health check duration: " + p.mHealthCheckDurationMs + "ms"); + pw.println("Health check state: " + p.toString(p.mHealthCheckState)); + pw.decreaseIndent(); + } + } + } + + /** @hide */ + @Retention(SOURCE) + @IntDef(value = { + HealthCheckState.ACTIVE, + HealthCheckState.INACTIVE, + HealthCheckState.PASSED, + HealthCheckState.FAILED}) + public @interface HealthCheckState { + // The package has not passed health check but has requested a health check + int ACTIVE = 0; + // The package has not passed health check and has not requested a health check + int INACTIVE = 1; + // The package has passed health check + int PASSED = 2; + // The package has failed health check + int FAILED = 3; + } + + MonitoredPackage newMonitoredPackage( + String name, long durationMs, boolean hasPassedHealthCheck) { + return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck, + new LongArrayQueue()); + } + + MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs, + boolean hasPassedHealthCheck, LongArrayQueue mitigationCalls) { + return new MonitoredPackage(name, durationMs, healthCheckDurationMs, + hasPassedHealthCheck, mitigationCalls); + } + + MonitoredPackage parseMonitoredPackage(TypedXmlPullParser parser) + throws XmlPullParserException { + String packageName = parser.getAttributeValue(null, ATTR_NAME); + long duration = parser.getAttributeLong(null, ATTR_DURATION); + long healthCheckDuration = parser.getAttributeLong(null, + ATTR_EXPLICIT_HEALTH_CHECK_DURATION); + boolean hasPassedHealthCheck = parser.getAttributeBoolean(null, ATTR_PASSED_HEALTH_CHECK); + LongArrayQueue mitigationCalls = parseLongArrayQueue( + parser.getAttributeValue(null, ATTR_MITIGATION_CALLS)); + return newMonitoredPackage(packageName, + duration, healthCheckDuration, hasPassedHealthCheck, mitigationCalls); + } + + /** + * Represents a package and its health check state along with the time + * it should be monitored for. + * + * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing + * instances of this class. + */ + class MonitoredPackage { + private final String mPackageName; + // Times when package failures happen sorted in ascending order + @GuardedBy("mLock") + private final LongArrayQueue mFailureHistory = new LongArrayQueue(); + // Times when an observer was called to mitigate this package's failure. Sorted in + // ascending order. + @GuardedBy("mLock") + private final LongArrayQueue mMitigationCalls; + // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after + // methods that could change the health check state: handleElapsedTimeLocked and + // tryPassHealthCheckLocked + private int mHealthCheckState = HealthCheckState.INACTIVE; + // Whether an explicit health check has passed. + // This value in addition with mHealthCheckDurationMs determines the health check state + // of the package, see #getHealthCheckStateLocked + @GuardedBy("mLock") + private boolean mHasPassedHealthCheck; + // System uptime duration to monitor package. + @GuardedBy("mLock") + private long mDurationMs; + // System uptime duration to check the result of an explicit health check + // Initially, MAX_VALUE until we get a value from the health check service + // and request health checks. + // This value in addition with mHasPassedHealthCheck determines the health check state + // of the package, see #getHealthCheckStateLocked + @GuardedBy("mLock") + private long mHealthCheckDurationMs = Long.MAX_VALUE; + + MonitoredPackage(String packageName, long durationMs, + long healthCheckDurationMs, boolean hasPassedHealthCheck, + LongArrayQueue mitigationCalls) { + mPackageName = packageName; + mDurationMs = durationMs; + mHealthCheckDurationMs = healthCheckDurationMs; + mHasPassedHealthCheck = hasPassedHealthCheck; + mMitigationCalls = mitigationCalls; + updateHealthCheckStateLocked(); + } + + /** Writes the salient fields to disk using {@code out}. + * @hide + */ + @GuardedBy("mLock") + public void writeLocked(TypedXmlSerializer out) throws IOException { + out.startTag(null, TAG_PACKAGE); + out.attribute(null, ATTR_NAME, getName()); + out.attributeLong(null, ATTR_DURATION, mDurationMs); + out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs); + out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck); + LongArrayQueue normalizedCalls = normalizeMitigationCalls(); + out.attribute(null, ATTR_MITIGATION_CALLS, longArrayQueueToString(normalizedCalls)); + out.endTag(null, TAG_PACKAGE); + } + + /** + * Increment package failures or resets failure count depending on the last package failure. + * + * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise + */ + @GuardedBy("mLock") + public boolean onFailureLocked() { + // Sliding window algorithm: find out if there exists a window containing failures >= + // mTriggerFailureCount. + final long now = mSystemClock.uptimeMillis(); + mFailureHistory.addLast(now); + while (now - mFailureHistory.peekFirst() > mTriggerFailureDurationMs) { + // Prune values falling out of the window + mFailureHistory.removeFirst(); + } + boolean failed = mFailureHistory.size() >= mTriggerFailureCount; + if (failed) { + mFailureHistory.clear(); + } + return failed; + } + + /** + * Notes the timestamp of a mitigation call into the observer. + */ + @GuardedBy("mLock") + public void noteMitigationCallLocked() { + mMitigationCalls.addLast(mSystemClock.uptimeMillis()); + } + + /** + * Prunes any mitigation calls outside of the de-escalation window, and returns the + * number of calls that are in the window afterwards. + * + * @return the number of mitigation calls made in the de-escalation window. + */ + @GuardedBy("mLock") + public int getMitigationCountLocked() { + try { + final long now = mSystemClock.uptimeMillis(); + while (now - mMitigationCalls.peekFirst() > DEFAULT_DEESCALATION_WINDOW_MS) { + mMitigationCalls.removeFirst(); + } + } catch (NoSuchElementException ignore) { + } + + return mMitigationCalls.size(); + } + + /** + * Before writing to disk, make the mitigation call timestamps relative to the current + * system uptime. This is because they need to be relative to the uptime which will reset + * at the next boot. + * + * @return a LongArrayQueue of the mitigation calls relative to the current system uptime. + */ + @GuardedBy("mLock") + public LongArrayQueue normalizeMitigationCalls() { + LongArrayQueue normalized = new LongArrayQueue(); + final long now = mSystemClock.uptimeMillis(); + for (int i = 0; i < mMitigationCalls.size(); i++) { + normalized.addLast(mMitigationCalls.get(i) - now); + } + return normalized; + } + + /** + * Sets the initial health check duration. + * + * @return the new health check state + */ + @GuardedBy("mLock") + public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) { + if (initialHealthCheckDurationMs <= 0) { + Slog.wtf(TAG, "Cannot set non-positive health check duration " + + initialHealthCheckDurationMs + "ms for package " + getName() + + ". Using total duration " + mDurationMs + "ms instead"); + initialHealthCheckDurationMs = mDurationMs; + } + if (mHealthCheckState == HealthCheckState.INACTIVE) { + // Transitions to ACTIVE + mHealthCheckDurationMs = initialHealthCheckDurationMs; + } + return updateHealthCheckStateLocked(); + } + + /** + * Updates the monitoring durations of the package. + * + * @return the new health check state + */ + @GuardedBy("mLock") + public int handleElapsedTimeLocked(long elapsedMs) { + if (elapsedMs <= 0) { + Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + getName()); + return mHealthCheckState; + } + // Transitions to FAILED if now <= 0 and health check not passed + mDurationMs -= elapsedMs; + if (mHealthCheckState == HealthCheckState.ACTIVE) { + // We only update health check durations if we have #setHealthCheckActiveLocked + // This ensures we don't leave the INACTIVE state for an unexpected elapsed time + // Transitions to FAILED if now <= 0 and health check not passed + mHealthCheckDurationMs -= elapsedMs; + } + return updateHealthCheckStateLocked(); + } + + /** Explicitly update the monitoring duration of the package. */ + @GuardedBy("mLock") + public void updateHealthCheckDuration(long newDurationMs) { + mDurationMs = newDurationMs; + } + + /** + * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED} + * if not yet {@link HealthCheckState.FAILED}. + * + * @return the new {@link HealthCheckState health check state} + */ + @GuardedBy("mLock") + @HealthCheckState + public int tryPassHealthCheckLocked() { + if (mHealthCheckState != HealthCheckState.FAILED) { + // FAILED is a final state so only pass if we haven't failed + // Transition to PASSED + mHasPassedHealthCheck = true; + } + return updateHealthCheckStateLocked(); + } + + /** Returns the monitored package name. */ + private String getName() { + return mPackageName; + } + + /** + * Returns the current {@link HealthCheckState health check state}. + */ + @GuardedBy("mLock") + @HealthCheckState + public int getHealthCheckStateLocked() { + return mHealthCheckState; + } + + /** + * Returns the shortest duration before the package should be scheduled for a prune. + * + * @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled + */ + @GuardedBy("mLock") + public long getShortestScheduleDurationMsLocked() { + // Consider health check duration only if #isPendingHealthChecksLocked is true + return Math.min(toPositive(mDurationMs), + isPendingHealthChecksLocked() + ? toPositive(mHealthCheckDurationMs) : Long.MAX_VALUE); + } + + /** + * Returns {@code true} if the total duration left to monitor the package is less than or + * equal to 0 {@code false} otherwise. + */ + @GuardedBy("mLock") + public boolean isExpiredLocked() { + return mDurationMs <= 0; + } + + /** + * Returns {@code true} if the package, {@link #getName} is expecting health check results + * {@code false} otherwise. + */ + @GuardedBy("mLock") + public boolean isPendingHealthChecksLocked() { + return mHealthCheckState == HealthCheckState.ACTIVE + || mHealthCheckState == HealthCheckState.INACTIVE; + } + + /** + * Updates the health check state based on {@link #mHasPassedHealthCheck} + * and {@link #mHealthCheckDurationMs}. + * + * @return the new {@link HealthCheckState health check state} + */ + @GuardedBy("mLock") + @HealthCheckState + private int updateHealthCheckStateLocked() { + int oldState = mHealthCheckState; + if (mHasPassedHealthCheck) { + // Set final state first to avoid ambiguity + mHealthCheckState = HealthCheckState.PASSED; + } else if (mHealthCheckDurationMs <= 0 || mDurationMs <= 0) { + // Set final state first to avoid ambiguity + mHealthCheckState = HealthCheckState.FAILED; + } else if (mHealthCheckDurationMs == Long.MAX_VALUE) { + mHealthCheckState = HealthCheckState.INACTIVE; + } else { + mHealthCheckState = HealthCheckState.ACTIVE; + } + + if (oldState != mHealthCheckState) { + Slog.i(TAG, "Updated health check state for package " + getName() + ": " + + toString(oldState) + " -> " + toString(mHealthCheckState)); + } + return mHealthCheckState; + } + + /** Returns a {@link String} representation of the current health check state. */ + private String toString(@HealthCheckState int state) { + switch (state) { + case HealthCheckState.ACTIVE: + return "ACTIVE"; + case HealthCheckState.INACTIVE: + return "INACTIVE"; + case HealthCheckState.PASSED: + return "PASSED"; + case HealthCheckState.FAILED: + return "FAILED"; + default: + return "UNKNOWN"; + } + } + + /** Returns {@code value} if it is greater than 0 or {@link Long#MAX_VALUE} otherwise. */ + private long toPositive(long value) { + return value > 0 ? value : Long.MAX_VALUE; + } + + /** Compares the equality of this object with another {@link MonitoredPackage}. */ + @VisibleForTesting + boolean isEqualTo(MonitoredPackage pkg) { + return (getName().equals(pkg.getName())) + && mDurationMs == pkg.mDurationMs + && mHasPassedHealthCheck == pkg.mHasPassedHealthCheck + && mHealthCheckDurationMs == pkg.mHealthCheckDurationMs + && (mMitigationCalls.toString()).equals(pkg.mMitigationCalls.toString()); + } + } + + @GuardedBy("mLock") + @SuppressWarnings("GuardedBy") + void saveAllObserversBootMitigationCountToMetadata(String filePath) { + HashMap<String, Integer> bootMitigationCounts = new HashMap<>(); + for (int i = 0; i < mAllObservers.size(); i++) { + final ObserverInternal observer = mAllObservers.valueAt(i); + bootMitigationCounts.put(observer.name, observer.getBootMitigationCount()); + } + + try { + FileOutputStream fileStream = new FileOutputStream(new File(filePath)); + ObjectOutputStream objectStream = new ObjectOutputStream(fileStream); + objectStream.writeObject(bootMitigationCounts); + objectStream.flush(); + objectStream.close(); + fileStream.close(); + } catch (Exception e) { + Slog.i(TAG, "Could not save observers metadata to file: " + e); + } + } + + /** + * Handles the thresholding logic for system server boots. + */ + class BootThreshold { + + private final int mBootTriggerCount; + private final long mTriggerWindow; + + BootThreshold(int bootTriggerCount, long triggerWindow) { + this.mBootTriggerCount = bootTriggerCount; + this.mTriggerWindow = triggerWindow; + } + + public void reset() { + setStart(0); + setCount(0); + } + + protected int getCount() { + return CrashRecoveryProperties.rescueBootCount().orElse(0); + } + + protected void setCount(int count) { + CrashRecoveryProperties.rescueBootCount(count); + } + + public long getStart() { + return CrashRecoveryProperties.rescueBootStart().orElse(0L); + } + + public int getMitigationCount() { + return CrashRecoveryProperties.bootMitigationCount().orElse(0); + } + + public void setStart(long start) { + CrashRecoveryProperties.rescueBootStart(getStartTime(start)); + } + + public void setMitigationStart(long start) { + CrashRecoveryProperties.bootMitigationStart(getStartTime(start)); + } + + public long getMitigationStart() { + return CrashRecoveryProperties.bootMitigationStart().orElse(0L); + } + + public void setMitigationCount(int count) { + CrashRecoveryProperties.bootMitigationCount(count); + } + + private static long constrain(long amount, long low, long high) { + return amount < low ? low : (amount > high ? high : amount); + } + + public long getStartTime(long start) { + final long now = mSystemClock.uptimeMillis(); + return constrain(start, 0, now); + } + + public void saveMitigationCountToMetadata() { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(METADATA_FILE))) { + writer.write(String.valueOf(getMitigationCount())); + } catch (Exception e) { + Slog.e(TAG, "Could not save metadata to file: " + e); + } + } + + public void readMitigationCountFromMetadataIfNecessary() { + File bootPropsFile = new File(METADATA_FILE); + if (bootPropsFile.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(METADATA_FILE))) { + String mitigationCount = reader.readLine(); + setMitigationCount(Integer.parseInt(mitigationCount)); + bootPropsFile.delete(); + } catch (Exception e) { + Slog.i(TAG, "Could not read metadata file: " + e); + } + } + } + + + /** Increments the boot counter, and returns whether the device is bootlooping. */ + @GuardedBy("mLock") + public boolean incrementAndTest() { + if (Flags.recoverabilityDetection()) { + readAllObserversBootMitigationCountIfNecessary(METADATA_FILE); + } else { + readMitigationCountFromMetadataIfNecessary(); + } + + final long now = mSystemClock.uptimeMillis(); + if (now - getStart() < 0) { + Slog.e(TAG, "Window was less than zero. Resetting start to current time."); + setStart(now); + setMitigationStart(now); + } + if (now - getMitigationStart() > DEFAULT_DEESCALATION_WINDOW_MS) { + setMitigationStart(now); + if (Flags.recoverabilityDetection()) { + resetAllObserversBootMitigationCount(); + } else { + setMitigationCount(0); + } + } + final long window = now - getStart(); + if (window >= mTriggerWindow) { + setCount(1); + setStart(now); + return false; + } else { + int count = getCount() + 1; + setCount(count); + EventLog.writeEvent(LOG_TAG_RESCUE_NOTE, Process.ROOT_UID, count, window); + if (Flags.recoverabilityDetection()) { + // After a reboot (e.g. by WARM_REBOOT or mainline rollback) we apply + // mitigations without waiting for DEFAULT_BOOT_LOOP_TRIGGER_COUNT. + return (count >= mBootTriggerCount) + || (performedMitigationsDuringWindow() && count > 1); + } + return count >= mBootTriggerCount; + } + } + + @GuardedBy("mLock") + private boolean performedMitigationsDuringWindow() { + for (ObserverInternal observerInternal: mAllObservers.values()) { + if (observerInternal.getBootMitigationCount() > 0) { + return true; + } + } + return false; + } + + @GuardedBy("mLock") + private void resetAllObserversBootMitigationCount() { + for (int i = 0; i < mAllObservers.size(); i++) { + final ObserverInternal observer = mAllObservers.valueAt(i); + observer.setBootMitigationCount(0); + } + saveAllObserversBootMitigationCountToMetadata(METADATA_FILE); + } + + @GuardedBy("mLock") + @SuppressWarnings("GuardedBy") + void readAllObserversBootMitigationCountIfNecessary(String filePath) { + File metadataFile = new File(filePath); + if (metadataFile.exists()) { + try { + FileInputStream fileStream = new FileInputStream(metadataFile); + ObjectInputStream objectStream = new ObjectInputStream(fileStream); + HashMap<String, Integer> bootMitigationCounts = + (HashMap<String, Integer>) objectStream.readObject(); + objectStream.close(); + fileStream.close(); + + for (int i = 0; i < mAllObservers.size(); i++) { + final ObserverInternal observer = mAllObservers.valueAt(i); + if (bootMitigationCounts.containsKey(observer.name)) { + observer.setBootMitigationCount( + bootMitigationCounts.get(observer.name)); + } + } + } catch (Exception e) { + Slog.i(TAG, "Could not read observer metadata file: " + e); + } + } + } + } + + /** + * Register broadcast receiver for shutdown. + * We would save the observer state to persist across boots. + * + * @hide + */ + public void registerShutdownBroadcastReceiver() { + BroadcastReceiver shutdownEventReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Only write if intent is relevant to device reboot or shutdown. + String intentAction = intent.getAction(); + if (ACTION_REBOOT.equals(intentAction) + || ACTION_SHUTDOWN.equals(intentAction)) { + writeNow(); + } + } + }; + + // Setup receiver for device reboots or shutdowns. + IntentFilter filter = new IntentFilter(ACTION_REBOOT); + filter.addAction(ACTION_SHUTDOWN); + mContext.registerReceiverForAllUsers(shutdownEventReceiver, filter, null, + /* run on main thread */ null); + } +} diff --git a/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java new file mode 100644 index 000000000000..f1b2f6b38efa --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/server/RescueParty.java @@ -0,0 +1,990 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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; + +import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.VersionedPackage; +import android.crashrecovery.flags.Flags; +import android.os.Build; +import android.os.Environment; +import android.os.PowerManager; +import android.os.RecoverySystem; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.sysprop.CrashRecoveryProperties; +import android.text.TextUtils; +import android.util.ArraySet; +import android.util.ArrayUtils; +import android.util.EventLog; +import android.util.FileUtils; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.PackageWatchdog.FailureReasons; +import com.android.server.PackageWatchdog.PackageHealthObserver; +import com.android.server.PackageWatchdog.PackageHealthObserverImpact; +import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; + +import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * Utilities to help rescue the system from crash loops. Callers are expected to + * report boot events and persistent app crashes, and if they happen frequently + * enough this class will slowly escalate through several rescue operations + * before finally rebooting and prompting the user if they want to wipe data as + * a last resort. + * + * @hide + */ +public class RescueParty { + @VisibleForTesting + static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; + @VisibleForTesting + static final int LEVEL_NONE = 0; + @VisibleForTesting + static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1; + @VisibleForTesting + static final int LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 2; + @VisibleForTesting + static final int LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 3; + @VisibleForTesting + static final int LEVEL_WARM_REBOOT = 4; + @VisibleForTesting + static final int LEVEL_FACTORY_RESET = 5; + @VisibleForTesting + static final int RESCUE_LEVEL_NONE = 0; + @VisibleForTesting + static final int RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET = 1; + @VisibleForTesting + static final int RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET = 2; + @VisibleForTesting + static final int RESCUE_LEVEL_WARM_REBOOT = 3; + @VisibleForTesting + static final int RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 4; + @VisibleForTesting + static final int RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES = 5; + @VisibleForTesting + static final int RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS = 6; + @VisibleForTesting + static final int RESCUE_LEVEL_FACTORY_RESET = 7; + + @IntDef(prefix = { "RESCUE_LEVEL_" }, value = { + RESCUE_LEVEL_NONE, + RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET, + RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET, + RESCUE_LEVEL_WARM_REBOOT, + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES, + RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS, + RESCUE_LEVEL_FACTORY_RESET + }) + @Retention(RetentionPolicy.SOURCE) + @interface RescueLevels {} + + @VisibleForTesting + static final String RESCUE_NON_REBOOT_LEVEL_LIMIT = "persist.sys.rescue_non_reboot_level_limit"; + @VisibleForTesting + static final int DEFAULT_RESCUE_NON_REBOOT_LEVEL_LIMIT = RESCUE_LEVEL_WARM_REBOOT - 1; + @VisibleForTesting + static final String TAG = "RescueParty"; + @VisibleForTesting + static final long DEFAULT_OBSERVING_DURATION_MS = TimeUnit.DAYS.toMillis(2); + @VisibleForTesting + static final int DEVICE_CONFIG_RESET_MODE = Settings.RESET_MODE_TRUSTED_DEFAULTS; + // The DeviceConfig namespace containing all RescueParty switches. + @VisibleForTesting + static final String NAMESPACE_CONFIGURATION = "configuration"; + @VisibleForTesting + static final String NAMESPACE_TO_PACKAGE_MAPPING_FLAG = + "namespace_to_package_mapping"; + @VisibleForTesting + static final long DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN = 1440; + + private static final String NAME = "rescue-party-observer"; + + private static final String PROP_DISABLE_RESCUE = "persist.sys.disable_rescue"; + private static final String PROP_VIRTUAL_DEVICE = "ro.hardware.virtual_device"; + private static final String PROP_DEVICE_CONFIG_DISABLE_FLAG = + "persist.device_config.configuration.disable_rescue_party"; + private static final String PROP_DISABLE_FACTORY_RESET_FLAG = + "persist.device_config.configuration.disable_rescue_party_factory_reset"; + private static final String PROP_THROTTLE_DURATION_MIN_FLAG = + "persist.device_config.configuration.rescue_party_throttle_duration_min"; + + private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + + /** + * EventLog tags used when logging into the event log. Note the values must be sync with + * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct + * name translation. + */ + private static final int LOG_TAG_RESCUE_SUCCESS = 2902; + private static final int LOG_TAG_RESCUE_FAILURE = 2903; + + /** Register the Rescue Party observer as a Package Watchdog health observer */ + public static void registerHealthObserver(Context context) { + PackageWatchdog.getInstance(context).registerHealthObserver( + RescuePartyObserver.getInstance(context)); + } + + private static boolean isDisabled() { + // Check if we're explicitly enabled for testing + if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) { + return false; + } + + // We're disabled if the DeviceConfig disable flag is set to true. + // This is in case that an emergency rollback of the feature is needed. + if (SystemProperties.getBoolean(PROP_DEVICE_CONFIG_DISABLE_FLAG, false)) { + Slog.v(TAG, "Disabled because of DeviceConfig flag"); + return true; + } + + // We're disabled on all engineering devices + if (Build.TYPE.equals("eng")) { + Slog.v(TAG, "Disabled because of eng build"); + return true; + } + + // We're disabled on userdebug devices connected over USB, since that's + // a decent signal that someone is actively trying to debug the device, + // or that it's in a lab environment. + if (Build.TYPE.equals("userdebug") && isUsbActive()) { + Slog.v(TAG, "Disabled because of active USB connection"); + return true; + } + + // One last-ditch check + if (SystemProperties.getBoolean(PROP_DISABLE_RESCUE, false)) { + Slog.v(TAG, "Disabled because of manual property"); + return true; + } + + return false; + } + + /** + * Check if we're currently attempting to reboot for a factory reset. This method must + * return true if RescueParty tries to reboot early during a boot loop, since the device + * will not be fully booted at this time. + */ + public static boolean isRecoveryTriggeredReboot() { + return isFactoryResetPropertySet() || isRebootPropertySet(); + } + + static boolean isFactoryResetPropertySet() { + return CrashRecoveryProperties.attemptingFactoryReset().orElse(false); + } + + static boolean isRebootPropertySet() { + return CrashRecoveryProperties.attemptingReboot().orElse(false); + } + + protected static long getLastFactoryResetTimeMs() { + return CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L); + } + + protected static int getMaxRescueLevelAttempted() { + return CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE); + } + + protected static void setFactoryResetProperty(boolean value) { + CrashRecoveryProperties.attemptingFactoryReset(value); + } + protected static void setRebootProperty(boolean value) { + CrashRecoveryProperties.attemptingReboot(value); + } + + protected static void setLastFactoryResetTimeMs(long value) { + CrashRecoveryProperties.lastFactoryResetTimeMs(value); + } + + protected static void setMaxRescueLevelAttempted(int level) { + CrashRecoveryProperties.maxRescueLevelAttempted(level); + } + + private static Set<String> getPresetNamespacesForPackages(List<String> packageNames) { + Set<String> resultSet = new ArraySet<String>(); + if (!Flags.deprecateFlagsAndSettingsResets()) { + try { + String flagVal = DeviceConfig.getString(NAMESPACE_CONFIGURATION, + NAMESPACE_TO_PACKAGE_MAPPING_FLAG, ""); + String[] mappingEntries = flagVal.split(","); + for (int i = 0; i < mappingEntries.length; i++) { + if (TextUtils.isEmpty(mappingEntries[i])) { + continue; + } + String[] splitEntry = mappingEntries[i].split(":"); + if (splitEntry.length != 2) { + throw new RuntimeException("Invalid mapping entry: " + mappingEntries[i]); + } + String namespace = splitEntry[0]; + String packageName = splitEntry[1]; + + if (packageNames.contains(packageName)) { + resultSet.add(namespace); + } + } + } catch (Exception e) { + resultSet.clear(); + Slog.e(TAG, "Failed to read preset package to namespaces mapping.", e); + } finally { + return resultSet; + } + } else { + return resultSet; + } + } + + @VisibleForTesting + static long getElapsedRealtime() { + return SystemClock.elapsedRealtime(); + } + + private static class RescuePartyMonitorCallback implements DeviceConfig.MonitorCallback { + Context mContext; + + RescuePartyMonitorCallback(Context context) { + this.mContext = context; + } + + public void onNamespaceUpdate(@NonNull String updatedNamespace) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + startObservingPackages(mContext, updatedNamespace); + } + } + + public void onDeviceConfigAccess(@NonNull String callingPackage, + @NonNull String namespace) { + + if (!Flags.deprecateFlagsAndSettingsResets()) { + RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess( + callingPackage, + namespace); + } + } + } + + private static void startObservingPackages(Context context, @NonNull String updatedNamespace) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + RescuePartyObserver rescuePartyObserver = RescuePartyObserver.getInstance(context); + Set<String> callingPackages = rescuePartyObserver.getCallingPackagesSet( + updatedNamespace); + if (callingPackages == null) { + return; + } + List<String> callingPackageList = new ArrayList<>(); + callingPackageList.addAll(callingPackages); + Slog.i(TAG, "Starting to observe: " + callingPackageList + ", updated namespace: " + + updatedNamespace); + PackageWatchdog.getInstance(context).startObservingHealth( + rescuePartyObserver, + callingPackageList, + DEFAULT_OBSERVING_DURATION_MS); + } + } + + private static int getMaxRescueLevel(boolean mayPerformReboot) { + if (Flags.recoverabilityDetection()) { + if (!mayPerformReboot + || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { + return SystemProperties.getInt(RESCUE_NON_REBOOT_LEVEL_LIMIT, + DEFAULT_RESCUE_NON_REBOOT_LEVEL_LIMIT); + } + return RESCUE_LEVEL_FACTORY_RESET; + } else { + if (!mayPerformReboot + || SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { + return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; + } + return LEVEL_FACTORY_RESET; + } + } + + private static int getMaxRescueLevel() { + if (!SystemProperties.getBoolean(PROP_DISABLE_FACTORY_RESET_FLAG, false)) { + return Level.factoryReset(); + } + return Level.reboot(); + } + + /** + * Get the rescue level to perform if this is the n-th attempt at mitigating failure. + * + * @param mitigationCount: the mitigation attempt number (1 = first attempt etc.) + * @param mayPerformReboot: whether or not a reboot and factory reset may be performed + * for the given failure. + * @return the rescue level for the n-th mitigation attempt. + */ + private static int getRescueLevel(int mitigationCount, boolean mayPerformReboot) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + if (mitigationCount == 1) { + return LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS; + } else if (mitigationCount == 2) { + return LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES; + } else if (mitigationCount == 3) { + return LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS; + } else if (mitigationCount == 4) { + return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_WARM_REBOOT); + } else if (mitigationCount >= 5) { + return Math.min(getMaxRescueLevel(mayPerformReboot), LEVEL_FACTORY_RESET); + } else { + Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); + return LEVEL_NONE; + } + } else { + if (mitigationCount == 1) { + return Level.reboot(); + } else if (mitigationCount >= 2) { + return Math.min(getMaxRescueLevel(), Level.factoryReset()); + } else { + Slog.w(TAG, "Expected positive mitigation count, was " + mitigationCount); + return LEVEL_NONE; + } + } + } + + /** + * Get the rescue level to perform if this is the n-th attempt at mitigating failure. + * When failedPackage is null then 1st and 2nd mitigation counts are redundant (scoped and + * all device config reset). Behaves as if one mitigation attempt was already done. + * + * @param mitigationCount the mitigation attempt number (1 = first attempt etc.). + * @param mayPerformReboot whether or not a reboot and factory reset may be performed + * for the given failure. + * @param failedPackage in case of bootloop this is null. + * @return the rescue level for the n-th mitigation attempt. + */ + private static @RescueLevels int getRescueLevel(int mitigationCount, boolean mayPerformReboot, + @Nullable VersionedPackage failedPackage) { + // Skipping RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET since it's not defined without a failed + // package. + if (failedPackage == null && mitigationCount > 0) { + mitigationCount += 1; + } + if (mitigationCount == 1) { + return RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET; + } else if (mitigationCount == 2) { + return RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET; + } else if (mitigationCount == 3) { + return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_WARM_REBOOT); + } else if (mitigationCount == 4) { + return Math.min(getMaxRescueLevel(mayPerformReboot), + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS); + } else if (mitigationCount == 5) { + return Math.min(getMaxRescueLevel(mayPerformReboot), + RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES); + } else if (mitigationCount == 6) { + return Math.min(getMaxRescueLevel(mayPerformReboot), + RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS); + } else if (mitigationCount >= 7) { + return Math.min(getMaxRescueLevel(mayPerformReboot), RESCUE_LEVEL_FACTORY_RESET); + } else { + return RESCUE_LEVEL_NONE; + } + } + + /** + * Get the rescue level to perform if this is the n-th attempt at mitigating failure. + * + * @param mitigationCount the mitigation attempt number (1 = first attempt etc.). + * @return the rescue level for the n-th mitigation attempt. + */ + private static @RescueLevels int getRescueLevel(int mitigationCount) { + if (mitigationCount == 1) { + return Level.reboot(); + } else if (mitigationCount >= 2) { + return Math.min(getMaxRescueLevel(), Level.factoryReset()); + } else { + return Level.none(); + } + } + + private static void executeRescueLevel(Context context, @Nullable String failedPackage, + int level) { + Slog.w(TAG, "Attempting rescue level " + levelToString(level)); + try { + executeRescueLevelInternal(context, level, failedPackage); + EventLog.writeEvent(LOG_TAG_RESCUE_SUCCESS, level); + String successMsg = "Finished rescue level " + levelToString(level); + if (!TextUtils.isEmpty(failedPackage)) { + successMsg += " for package " + failedPackage; + } + logCrashRecoveryEvent(Log.DEBUG, successMsg); + } catch (Throwable t) { + logRescueException(level, failedPackage, t); + } + } + + private static void executeRescueLevelInternal(Context context, int level, @Nullable + String failedPackage) throws Exception { + if (Flags.recoverabilityDetection()) { + executeRescueLevelInternalNew(context, level, failedPackage); + } else { + executeRescueLevelInternalOld(context, level, failedPackage); + } + } + + private static void executeRescueLevelInternalOld(Context context, int level, @Nullable + String failedPackage) throws Exception { + CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED, + level, levelToString(level)); + // Try our best to reset all settings possible, and once finished + // rethrow any exception that we encountered + Exception res = null; + switch (level) { + case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + break; + case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + break; + case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + break; + case LEVEL_WARM_REBOOT: + executeWarmReboot(context, level, failedPackage); + break; + case LEVEL_FACTORY_RESET: + // Before the completion of Reboot, if any crash happens then PackageWatchdog + // escalates to next level i.e. factory reset, as they happen in separate threads. + // Adding a check to prevent factory reset to execute before above reboot completes. + // Note: this reboot property is not persistent resets after reboot is completed. + if (isRebootPropertySet()) { + return; + } + executeFactoryReset(context, level, failedPackage); + break; + } + + if (res != null) { + throw res; + } + } + + private static void executeRescueLevelInternalNew(Context context, @RescueLevels int level, + @Nullable String failedPackage) throws Exception { + CrashRecoveryStatsLog.write(CrashRecoveryStatsLog.RESCUE_PARTY_RESET_REPORTED, + level, levelToString(level)); + switch (level) { + case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET: + break; + case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET: + break; + case RESCUE_LEVEL_WARM_REBOOT: + executeWarmReboot(context, level, failedPackage); + break; + case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + // do nothing + break; + case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + // do nothing + break; + case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + // do nothing + break; + case RESCUE_LEVEL_FACTORY_RESET: + // Before the completion of Reboot, if any crash happens then PackageWatchdog + // escalates to next level i.e. factory reset, as they happen in separate threads. + // Adding a check to prevent factory reset to execute before above reboot completes. + // Note: this reboot property is not persistent resets after reboot is completed. + if (isRebootPropertySet()) { + return; + } + executeFactoryReset(context, level, failedPackage); + break; + } + } + + private static void executeWarmReboot(Context context, int level, + @Nullable String failedPackage) { + if (Flags.deprecateFlagsAndSettingsResets()) { + if (shouldThrottleReboot()) { + return; + } + } + + // Request the reboot from a separate thread to avoid deadlock on PackageWatchdog + // when device shutting down. + setRebootProperty(true); + + if (Flags.synchronousRebootInRescueParty()) { + try { + PowerManager pm = context.getSystemService(PowerManager.class); + if (pm != null) { + pm.reboot(TAG); + } + } catch (Throwable t) { + logRescueException(level, failedPackage, t); + } + } else { + Runnable runnable = () -> { + try { + PowerManager pm = context.getSystemService(PowerManager.class); + if (pm != null) { + pm.reboot(TAG); + } + } catch (Throwable t) { + logRescueException(level, failedPackage, t); + } + }; + Thread thread = new Thread(runnable); + thread.start(); + } + } + + private static void executeFactoryReset(Context context, int level, + @Nullable String failedPackage) { + if (Flags.deprecateFlagsAndSettingsResets()) { + if (shouldThrottleReboot()) { + return; + } + } + setFactoryResetProperty(true); + long now = System.currentTimeMillis(); + setLastFactoryResetTimeMs(now); + + if (Flags.synchronousRebootInRescueParty()) { + try { + RecoverySystem.rebootPromptAndWipeUserData(context, TAG + "," + failedPackage); + } catch (Throwable t) { + logRescueException(level, failedPackage, t); + } + } else { + Runnable runnable = new Runnable() { + @Override + public void run() { + try { + RecoverySystem.rebootPromptAndWipeUserData(context, + TAG + "," + failedPackage); + } catch (Throwable t) { + logRescueException(level, failedPackage, t); + } + } + }; + Thread thread = new Thread(runnable); + thread.start(); + } + } + + + private static String getCompleteMessage(Throwable t) { + final StringBuilder builder = new StringBuilder(); + builder.append(t.getMessage()); + while ((t = t.getCause()) != null) { + builder.append(": ").append(t.getMessage()); + } + return builder.toString(); + } + + private static void logRescueException(int level, @Nullable String failedPackageName, + Throwable t) { + final String msg = getCompleteMessage(t); + EventLog.writeEvent(LOG_TAG_RESCUE_FAILURE, level, msg); + String failureMsg = "Failed rescue level " + levelToString(level); + if (!TextUtils.isEmpty(failedPackageName)) { + failureMsg += " for package " + failedPackageName; + } + logCrashRecoveryEvent(Log.ERROR, failureMsg + ": " + msg); + } + + private static int mapRescueLevelToUserImpact(int rescueLevel) { + if (Flags.recoverabilityDetection()) { + switch (rescueLevel) { + case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10; + case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_40; + case RESCUE_LEVEL_WARM_REBOOT: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50; + case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_71; + case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_75; + case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_80; + case RESCUE_LEVEL_FACTORY_RESET: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_100; + default: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + } else { + switch (rescueLevel) { + case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_10; + case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + case LEVEL_WARM_REBOOT: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_50; + case LEVEL_FACTORY_RESET: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_100; + default: + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + } + } + + /** + * Handle mitigation action for package failures. This observer will be register to Package + * Watchdog and will receive calls about package failures. This observer is persistent so it + * may choose to mitigate failures for packages it has not explicitly asked to observe. + */ + public static class RescuePartyObserver implements PackageHealthObserver { + + private final Context mContext; + private final Map<String, Set<String>> mCallingPackageNamespaceSetMap = new HashMap<>(); + private final Map<String, Set<String>> mNamespaceCallingPackageSetMap = new HashMap<>(); + + @GuardedBy("RescuePartyObserver.class") + static RescuePartyObserver sRescuePartyObserver; + + private RescuePartyObserver(Context context) { + mContext = context; + } + + /** Creates or gets singleton instance of RescueParty. */ + public static RescuePartyObserver getInstance(Context context) { + synchronized (RescuePartyObserver.class) { + if (sRescuePartyObserver == null) { + sRescuePartyObserver = new RescuePartyObserver(context); + } + return sRescuePartyObserver; + } + } + + /** Gets singleton instance. It returns null if the instance is not created yet.*/ + @Nullable + public static RescuePartyObserver getInstanceIfCreated() { + synchronized (RescuePartyObserver.class) { + return sRescuePartyObserver; + } + } + + @VisibleForTesting + static void reset() { + synchronized (RescuePartyObserver.class) { + sRescuePartyObserver = null; + } + } + + @Override + public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, + @FailureReasons int failureReason, int mitigationCount) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + if (!isDisabled() && (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH + || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING)) { + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + impact = mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + mayPerformReboot(failedPackage), failedPackage)); + } else { + impact = mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); + } + } else { + impact = mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + mayPerformReboot(failedPackage))); + } + } + + Slog.i(TAG, "Checking available remediations for health check failure." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failureReason: " + failureReason + + " available impact: " + impact); + return impact; + } + + @Override + public boolean execute(@Nullable VersionedPackage failedPackage, + @FailureReasons int failureReason, int mitigationCount) { + if (isDisabled()) { + return false; + } + Slog.i(TAG, "Executing remediation." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failureReason: " + failureReason + + " mitigationCount: " + mitigationCount); + if (failureReason == PackageWatchdog.FAILURE_REASON_APP_CRASH + || failureReason == PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING) { + final int level; + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage), + failedPackage); + } else { + level = getRescueLevel(mitigationCount); + } + } else { + level = getRescueLevel(mitigationCount, mayPerformReboot(failedPackage)); + } + executeRescueLevel(mContext, + failedPackage == null ? null : failedPackage.getPackageName(), level); + return true; + } else { + return false; + } + } + + @Override + public boolean isPersistent() { + return true; + } + + @Override + public boolean mayObservePackage(String packageName) { + PackageManager pm = mContext.getPackageManager(); + try { + // A package is a module if this is non-null + if (pm.getModuleInfo(packageName, 0) != null) { + return true; + } + } catch (PackageManager.NameNotFoundException | IllegalStateException ignore) { + } + + return isPersistentSystemApp(packageName); + } + + @Override + public int onBootLoop(int mitigationCount) { + if (isDisabled()) { + return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, + true, /*failedPackage=*/ null)); + } else { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); + } + } else { + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount, true)); + } + } + + @Override + public boolean executeBootLoopMitigation(int mitigationCount) { + if (isDisabled()) { + return false; + } + boolean mayPerformReboot = !shouldThrottleReboot(); + final int level; + if (Flags.recoverabilityDetection()) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + level = getRescueLevel(mitigationCount, mayPerformReboot, + /*failedPackage=*/ null); + } else { + level = getRescueLevel(mitigationCount); + } + } else { + level = getRescueLevel(mitigationCount, mayPerformReboot); + } + executeRescueLevel(mContext, /*failedPackage=*/ null, level); + return true; + } + + @Override + public String getUniqueIdentifier() { + return NAME; + } + + /** + * Returns {@code true} if the failing package is non-null and performing a reboot or + * prompting a factory reset is an acceptable mitigation strategy for the package's + * failure, {@code false} otherwise. + */ + private boolean mayPerformReboot(@Nullable VersionedPackage failingPackage) { + if (failingPackage == null) { + return false; + } + if (shouldThrottleReboot()) { + return false; + } + + return isPersistentSystemApp(failingPackage.getPackageName()); + } + + private boolean isPersistentSystemApp(@NonNull String packageName) { + PackageManager pm = mContext.getPackageManager(); + try { + ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + private synchronized void recordDeviceConfigAccess(@NonNull String callingPackage, + @NonNull String namespace) { + if (!Flags.deprecateFlagsAndSettingsResets()) { + // Record it in calling packages to namespace map + Set<String> namespaceSet = mCallingPackageNamespaceSetMap.get(callingPackage); + if (namespaceSet == null) { + namespaceSet = new ArraySet<>(); + mCallingPackageNamespaceSetMap.put(callingPackage, namespaceSet); + } + namespaceSet.add(namespace); + // Record it in namespace to calling packages map + Set<String> callingPackageSet = mNamespaceCallingPackageSetMap.get(namespace); + if (callingPackageSet == null) { + callingPackageSet = new ArraySet<>(); + } + callingPackageSet.add(callingPackage); + mNamespaceCallingPackageSetMap.put(namespace, callingPackageSet); + } + } + + private synchronized Set<String> getAffectedNamespaceSet(String failedPackage) { + return mCallingPackageNamespaceSetMap.get(failedPackage); + } + + private synchronized Set<String> getAllAffectedNamespaceSet() { + return new HashSet<String>(mNamespaceCallingPackageSetMap.keySet()); + } + + private synchronized Set<String> getCallingPackagesSet(String namespace) { + return mNamespaceCallingPackageSetMap.get(namespace); + } + } + + /** + * Returns {@code true} if Rescue Party is allowed to attempt a reboot or factory reset. + * Will return {@code false} if a factory reset was already offered recently. + */ + private static boolean shouldThrottleReboot() { + Long lastResetTime = getLastFactoryResetTimeMs(); + long now = System.currentTimeMillis(); + long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG, + DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN); + return now < lastResetTime + TimeUnit.MINUTES.toMillis(throttleDurationMin); + } + + private static int[] getAllUserIds() { + int systemUserId = UserHandle.SYSTEM.getIdentifier(); + int[] userIds = { systemUserId }; + try { + for (File file : FileUtils.listFilesOrEmpty(Environment.getDataSystemDeDirectory())) { + try { + final int userId = Integer.parseInt(file.getName()); + if (userId != systemUserId) { + userIds = ArrayUtils.appendInt(userIds, userId); + } + } catch (NumberFormatException ignored) { + } + } + } catch (Throwable t) { + Slog.w(TAG, "Trouble discovering users", t); + } + return userIds; + } + + /** + * Hacky test to check if the device has an active USB connection, which is + * a good proxy for someone doing local development work. + */ + private static boolean isUsbActive() { + if (SystemProperties.getBoolean(PROP_VIRTUAL_DEVICE, false)) { + Slog.v(TAG, "Assuming virtual device is connected over USB"); + return true; + } + try { + final String state = FileUtils + .readTextFile(new File("/sys/class/android_usb/android0/state"), 128, ""); + return "CONFIGURED".equals(state.trim()); + } catch (Throwable t) { + Slog.w(TAG, "Failed to determine if device was on USB", t); + return false; + } + } + + private static class Level { + static int none() { + return Flags.recoverabilityDetection() ? RESCUE_LEVEL_NONE : LEVEL_NONE; + } + + static int reboot() { + return Flags.recoverabilityDetection() ? RESCUE_LEVEL_WARM_REBOOT : LEVEL_WARM_REBOOT; + } + + static int factoryReset() { + return Flags.recoverabilityDetection() + ? RESCUE_LEVEL_FACTORY_RESET + : LEVEL_FACTORY_RESET; + } + } + + private static String levelToString(int level) { + if (Flags.recoverabilityDetection()) { + switch (level) { + case RESCUE_LEVEL_NONE: + return "NONE"; + case RESCUE_LEVEL_SCOPED_DEVICE_CONFIG_RESET: + return "SCOPED_DEVICE_CONFIG_RESET"; + case RESCUE_LEVEL_ALL_DEVICE_CONFIG_RESET: + return "ALL_DEVICE_CONFIG_RESET"; + case RESCUE_LEVEL_WARM_REBOOT: + return "WARM_REBOOT"; + case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + return "RESET_SETTINGS_UNTRUSTED_DEFAULTS"; + case RESCUE_LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + return "RESET_SETTINGS_UNTRUSTED_CHANGES"; + case RESCUE_LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + return "RESET_SETTINGS_TRUSTED_DEFAULTS"; + case RESCUE_LEVEL_FACTORY_RESET: + return "FACTORY_RESET"; + default: + return Integer.toString(level); + } + } else { + switch (level) { + case LEVEL_NONE: + return "NONE"; + case LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS: + return "RESET_SETTINGS_UNTRUSTED_DEFAULTS"; + case LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES: + return "RESET_SETTINGS_UNTRUSTED_CHANGES"; + case LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS: + return "RESET_SETTINGS_TRUSTED_DEFAULTS"; + case LEVEL_WARM_REBOOT: + return "WARM_REBOOT"; + case LEVEL_FACTORY_RESET: + return "FACTORY_RESET"; + default: + return Integer.toString(level); + } + } + } +} diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryModule.java b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryModule.java index 8a81aaa1e636..8a81aaa1e636 100644 --- a/services/core/java/com/android/server/crashrecovery/CrashRecoveryModule.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryModule.java diff --git a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryUtils.java index 2e2a93776f9d..2e2a93776f9d 100644 --- a/services/core/java/com/android/server/crashrecovery/CrashRecoveryUtils.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/crashrecovery/CrashRecoveryUtils.java 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 new file mode 100644 index 000000000000..2931652ee081 --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -0,0 +1,786 @@ +/* + * 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.server.rollback; + +import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.WorkerThread; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.VersionedPackage; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; +import android.content.rollback.RollbackManager; +import android.crashrecovery.flags.Flags; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.PowerManager; +import android.os.SystemProperties; +import android.sysprop.CrashRecoveryProperties; +import android.util.ArraySet; +import android.util.FileUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; +import com.android.server.PackageWatchdog; +import com.android.server.PackageWatchdog.FailureReasons; +import com.android.server.PackageWatchdog.PackageHealthObserver; +import com.android.server.PackageWatchdog.PackageHealthObserverImpact; +import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +/** + * {@link PackageHealthObserver} for {@link RollbackManagerService}. + * This class monitors crashes and triggers RollbackManager rollback accordingly. + * It also monitors native crashes for some short while after boot. + * + * @hide + */ +public final class RollbackPackageHealthObserver implements PackageHealthObserver { + private static final String TAG = "RollbackPackageHealthObserver"; + private static final String NAME = "rollback-observer"; + private static final String CLASS_NAME = RollbackPackageHealthObserver.class.getName(); + + private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + + private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = + "persist.device_config.configuration.disable_high_impact_rollback"; + + private final Context mContext; + private final Handler mHandler; + private final File mLastStagedRollbackIdsFile; + private final File mTwoPhaseRollbackEnabledFile; + // Staged rollback ids that have been committed but their session is not yet ready + private final Set<Integer> mPendingStagedRollbackIds = new ArraySet<>(); + // True if needing to roll back only rebootless apexes when native crash happens + private boolean mTwoPhaseRollbackEnabled; + + @VisibleForTesting + public RollbackPackageHealthObserver(@NonNull Context context) { + mContext = context; + HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + File dataDir = new File(Environment.getDataDirectory(), "rollback-observer"); + dataDir.mkdirs(); + mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids"); + mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled"); + PackageWatchdog.getInstance(mContext).registerHealthObserver(this); + + if (SystemProperties.getBoolean("sys.boot_completed", false)) { + // Load the value from the file if system server has crashed and restarted + mTwoPhaseRollbackEnabled = readBoolean(mTwoPhaseRollbackEnabledFile); + } else { + // Disable two-phase rollback for a normal reboot. We assume the rebootless apex + // installed before reboot is stable if native crash didn't happen. + mTwoPhaseRollbackEnabled = false; + writeBoolean(mTwoPhaseRollbackEnabledFile, false); + } + } + + @Override + public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, + @FailureReasons int failureReason, int mitigationCount) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW); + if (!lowImpactRollbacks.isEmpty()) { + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + // For native crashes, we will directly roll back any available rollbacks at low + // impact level + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (getRollbackForPackage(failedPackage, lowImpactRollbacks) != null) { + // Rollback is available for crashing low impact package + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else { + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + } + } + } else { + boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class) + .getAvailableRollbacks().isEmpty(); + + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH + && anyRollbackAvailable) { + // For native crashes, we will directly roll back any available rollbacks + // Note: For non-native crashes the rollback-all step has higher impact + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (getAvailableRollback(failedPackage) != null) { + // Rollback is available, we may get a callback into #execute + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (anyRollbackAvailable) { + // If any rollbacks are available, we will commit them + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + } + } + + Slog.i(TAG, "Checking available remediations for health check failure." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " failureReason: " + failureReason + + " available impact: " + impact); + return impact; + } + + @Override + public boolean execute(@Nullable VersionedPackage failedPackage, + @FailureReasons int rollbackReason, int mitigationCount) { + Slog.i(TAG, "Executing remediation." + + " failedPackage: " + + (failedPackage == null ? null : failedPackage.getPackageName()) + + " rollbackReason: " + rollbackReason + + " mitigationCount: " + mitigationCount); + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + return true; + } + + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackInfo rollback = getRollbackForPackage(failedPackage, lowImpactRollbacks); + if (rollback != null) { + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else if (!lowImpactRollbacks.isEmpty()) { + // Apply all available low impact rollbacks. + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + } + } else { + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + mHandler.post(() -> rollbackAll(rollbackReason)); + return true; + } + + RollbackInfo rollback = getAvailableRollback(failedPackage); + if (rollback != null) { + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else { + mHandler.post(() -> rollbackAll(rollbackReason)); + } + } + + // Assume rollbacks executed successfully + return true; + } + + @Override + public int onBootLoop(int mitigationCount) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + if (!availableRollbacks.isEmpty()) { + impact = getUserImpactBasedOnRollbackImpactLevel(availableRollbacks); + } + } + return impact; + } + + @Override + public boolean executeBootLoopMitigation(int mitigationCount) { + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + + triggerLeastImpactLevelRollback(availableRollbacks, + PackageWatchdog.FAILURE_REASON_BOOT_LOOP); + return true; + } + return false; + } + + @Override + @NonNull + public String getUniqueIdentifier() { + return NAME; + } + + @Override + public boolean isPersistent() { + return true; + } + + @Override + public boolean mayObservePackage(@NonNull String packageName) { + if (getAvailableRollbacks().isEmpty()) { + return false; + } + return isPersistentSystemApp(packageName); + } + + private List<RollbackInfo> getAvailableRollbacks() { + return mContext.getSystemService(RollbackManager.class).getAvailableRollbacks(); + } + + private boolean isPersistentSystemApp(@NonNull String packageName) { + PackageManager pm = mContext.getPackageManager(); + try { + ApplicationInfo info = pm.getApplicationInfo(packageName, 0); + return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + private void assertInWorkerThread() { + 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) { + mHandler.post(() -> { + // Enable two-phase rollback when a rebootless apex rollback is made available. + // We assume the rebootless apex is stable and is less likely to be the cause + // if native crash doesn't happen before reboot. So we will clear the flag and disable + // two-phase rollback after reboot. + if (isRebootlessApex(rollback)) { + mTwoPhaseRollbackEnabled = true; + writeBoolean(mTwoPhaseRollbackEnabledFile, true); + } + }); + } + + private static boolean isRebootlessApex(RollbackInfo rollback) { + if (!rollback.isStaged()) { + for (PackageRollbackInfo info : rollback.getPackages()) { + if (info.isApex()) { + return true; + } + } + } + return false; + } + + /** Verifies the rollback state after a reboot and schedules polling for sometime after reboot + * to check for native crashes and mitigate them if needed. + */ + @AnyThread + public void onBootCompletedAsync() { + mHandler.post(()->onBootCompleted()); + } + + @WorkerThread + private void onBootCompleted() { + assertInWorkerThread(); + + RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); + if (!rollbackManager.getAvailableRollbacks().isEmpty()) { + // TODO(gavincorkery): Call into Package Watchdog from outside the observer + PackageWatchdog.getInstance(mContext).scheduleCheckAndMitigateNativeCrashes(); + } + + SparseArray<String> rollbackIds = popLastStagedRollbackIds(); + for (int i = 0; i < rollbackIds.size(); i++) { + WatchdogRollbackLogger.logRollbackStatusOnBoot(mContext, + rollbackIds.keyAt(i), rollbackIds.valueAt(i), + rollbackManager.getRecentlyCommittedRollbacks()); + } + } + + @AnyThread + private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) { + RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); + for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) { + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) { + return rollback; + } + // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have + // to rely on complicated reasoning as below + + // Due to b/147666157, for apk in apex, we do not know the version we are rolling + // back from. But if a package X is embedded in apex A exclusively (not embedded in + // any other apex), which is not guaranteed, then it is sufficient to check only + // package names here, as the version of failedPackage and the PackageRollbackInfo + // can't be different. If failedPackage has a higher version, then it must have + // been updated somehow. There are two ways: it was updated by an update of apex A + // or updated directly as apk. In both cases, this rollback would have gotten + // expired when onPackageReplaced() was called. Since the rollback exists, it has + // same version as failedPackage. + if (packageRollback.isApkInApex() + && packageRollback.getVersionRolledBackFrom().getPackageName() + .equals(failedPackage.getPackageName())) { + return rollback; + } + } + } + return null; + } + + @AnyThread + private RollbackInfo getRollbackForPackage(@Nullable VersionedPackage failedPackage, + List<RollbackInfo> availableRollbacks) { + if (failedPackage == null) { + return null; + } + + for (RollbackInfo rollback : availableRollbacks) { + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) { + return rollback; + } + // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have + // to rely on complicated reasoning as below + + // Due to b/147666157, for apk in apex, we do not know the version we are rolling + // back from. But if a package X is embedded in apex A exclusively (not embedded in + // any other apex), which is not guaranteed, then it is sufficient to check only + // package names here, as the version of failedPackage and the PackageRollbackInfo + // can't be different. If failedPackage has a higher version, then it must have + // been updated somehow. There are two ways: it was updated by an update of apex A + // or updated directly as apk. In both cases, this rollback would have gotten + // expired when onPackageReplaced() was called. Since the rollback exists, it has + // same version as failedPackage. + if (packageRollback.isApkInApex() + && packageRollback.getVersionRolledBackFrom().getPackageName() + .equals(failedPackage.getPackageName())) { + return rollback; + } + } + } + return null; + } + + /** + * Returns {@code true} if staged session associated with {@code rollbackId} was marked + * as handled, {@code false} if already handled. + */ + @WorkerThread + private boolean markStagedSessionHandled(int rollbackId) { + assertInWorkerThread(); + return mPendingStagedRollbackIds.remove(rollbackId); + } + + /** + * Returns {@code true} if all pending staged rollback sessions were marked as handled, + * {@code false} if there is any left. + */ + @WorkerThread + private boolean isPendingStagedSessionsEmpty() { + assertInWorkerThread(); + return mPendingStagedRollbackIds.isEmpty(); + } + + private static boolean readBoolean(File file) { + try (FileInputStream fis = new FileInputStream(file)) { + return fis.read() == 1; + } catch (IOException ignore) { + return false; + } + } + + private static void writeBoolean(File file, boolean value) { + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(value ? 1 : 0); + fos.flush(); + FileUtils.sync(fos); + } catch (IOException ignore) { + } + } + + @WorkerThread + private void saveStagedRollbackId(int stagedRollbackId, @Nullable VersionedPackage logPackage) { + assertInWorkerThread(); + writeStagedRollbackId(mLastStagedRollbackIdsFile, stagedRollbackId, logPackage); + } + + static void writeStagedRollbackId(File file, int stagedRollbackId, + @Nullable VersionedPackage logPackage) { + try { + FileOutputStream fos = new FileOutputStream(file, true); + PrintWriter pw = new PrintWriter(fos); + String logPackageName = logPackage != null ? logPackage.getPackageName() : ""; + pw.append(String.valueOf(stagedRollbackId)).append(",").append(logPackageName); + pw.println(); + pw.flush(); + FileUtils.sync(fos); + pw.close(); + } catch (IOException e) { + Slog.e(TAG, "Failed to save last staged rollback id", e); + file.delete(); + } + } + + @WorkerThread + private SparseArray<String> popLastStagedRollbackIds() { + assertInWorkerThread(); + try { + return readStagedRollbackIds(mLastStagedRollbackIdsFile); + } finally { + mLastStagedRollbackIdsFile.delete(); + } + } + + static SparseArray<String> readStagedRollbackIds(File file) { + SparseArray<String> result = new SparseArray<>(); + try { + String line; + BufferedReader reader = new BufferedReader(new FileReader(file)); + while ((line = reader.readLine()) != null) { + // Each line is of the format: "id,logging_package" + String[] values = line.trim().split(","); + String rollbackId = values[0]; + String logPackageName = ""; + if (values.length > 1) { + logPackageName = values[1]; + } + result.put(Integer.parseInt(rollbackId), logPackageName); + } + } catch (Exception ignore) { + return new SparseArray<>(); + } + return result; + } + + + /** + * Returns true if the package name is the name of a module. + */ + @AnyThread + private boolean isModule(String packageName) { + // Check if the package is listed among the system modules or is an + // APK inside an updatable APEX. + try { + final PackageInfo pkg = mContext.getPackageManager() + .getPackageInfo(packageName, 0 /* flags */); + String apexPackageName = pkg.getApexPackageName(); + if (apexPackageName != null) { + packageName = apexPackageName; + } + + return pm.getModuleInfo(packageName, 0 /* flags */) != null; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + /** + * Rolls back the session that owns {@code failedPackage} + * + * @param rollback {@code rollbackInfo} of the {@code failedPackage} + * @param failedPackage the package that needs to be rolled back + */ + @WorkerThread + private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage, + @FailureReasons int rollbackReason) { + assertInWorkerThread(); + String failedPackageName = (failedPackage == null ? null : failedPackage.getPackageName()); + + Slog.i(TAG, "Rolling back package. RollbackId: " + rollback.getRollbackId() + + " failedPackage: " + failedPackageName + + " rollbackReason: " + rollbackReason); + logCrashRecoveryEvent(Log.DEBUG, String.format("Rolling back %s. Reason: %s", + failedPackageName, rollbackReason)); + final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); + int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason); + final String failedPackageToLog; + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + failedPackageToLog = SystemProperties.get( + "sys.init.updatable_crashing_process_name", ""); + } else { + failedPackageToLog = failedPackage.getPackageName(); + } + VersionedPackage logPackageTemp = null; + if (isModule(failedPackage.getPackageName())) { + logPackageTemp = WatchdogRollbackLogger.getLogPackage(mContext, failedPackage); + } + + final VersionedPackage logPackage = logPackageTemp; + WatchdogRollbackLogger.logEvent(logPackage, + CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE, + reasonToLog, failedPackageToLog); + + Consumer<Intent> onResult = result -> { + assertInWorkerThread(); + int status = result.getIntExtra(RollbackManager.EXTRA_STATUS, + RollbackManager.STATUS_FAILURE); + if (status == RollbackManager.STATUS_SUCCESS) { + if (rollback.isStaged()) { + int rollbackId = rollback.getRollbackId(); + saveStagedRollbackId(rollbackId, logPackage); + WatchdogRollbackLogger.logEvent(logPackage, + CrashRecoveryStatsLog + .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED, + reasonToLog, failedPackageToLog); + + } else { + WatchdogRollbackLogger.logEvent(logPackage, + CrashRecoveryStatsLog + .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + reasonToLog, failedPackageToLog); + } + } else { + WatchdogRollbackLogger.logEvent(logPackage, + CrashRecoveryStatsLog + .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + reasonToLog, failedPackageToLog); + } + if (rollback.isStaged()) { + markStagedSessionHandled(rollback.getRollbackId()); + // Wait for all pending staged sessions to get handled before rebooting. + if (isPendingStagedSessionsEmpty()) { + CrashRecoveryProperties.attemptingReboot(true); + mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); + } + } + }; + + // Define a BroadcastReceiver to handle the result + BroadcastReceiver rollbackReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent result) { + mHandler.post(() -> onResult.accept(result)); + } + }; + + String intentActionName = CLASS_NAME + rollback.getRollbackId(); + // Register the BroadcastReceiver + mContext.registerReceiver(rollbackReceiver, + new IntentFilter(intentActionName), + Context.RECEIVER_NOT_EXPORTED); + + Intent intentReceiver = new Intent(intentActionName); + intentReceiver.putExtra("rollbackId", rollback.getRollbackId()); + intentReceiver.setPackage(mContext.getPackageName()); + intentReceiver.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + + PendingIntent rollbackPendingIntent = PendingIntent.getBroadcast(mContext, + rollback.getRollbackId(), + intentReceiver, + PendingIntent.FLAG_MUTABLE); + + rollbackManager.commitRollback(rollback.getRollbackId(), + Collections.singletonList(failedPackage), + rollbackPendingIntent.getIntentSender()); + } + + /** + * Two-phase rollback: + * 1. roll back rebootless apexes first + * 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done + * + * This approach gives us a better chance to correctly attribute native crash to rebootless + * apex update without rolling back Mainline updates which might contains critical security + * fixes. + */ + @WorkerThread + private boolean useTwoPhaseRollback(List<RollbackInfo> rollbacks) { + assertInWorkerThread(); + if (!mTwoPhaseRollbackEnabled) { + return false; + } + + Slog.i(TAG, "Rolling back all rebootless APEX rollbacks"); + boolean found = false; + for (RollbackInfo rollback : rollbacks) { + if (isRebootlessApex(rollback)) { + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + found = true; + } + } + return found; + } + + /** + * Rollback the package that has minimum rollback impact level. + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollback + */ + private void triggerLeastImpactLevelRollback(List<RollbackInfo> availableRollbacks, + @FailureReasons int rollbackReason) { + int minRollbackImpactLevel = getMinRollbackImpactLevel(availableRollbacks); + + if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_LOW) { + // Apply all available low impact rollbacks. + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) { + // Check disable_high_impact_rollback device config before performing rollback + if (SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { + return; + } + // Rollback one package at a time. If that doesn't resolve the issue, rollback + // next with same impact level. + mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason)); + } + } + + /** + * sort the available high impact rollbacks by first package name to have a deterministic order. + * Apply the first available rollback. + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollback + */ + @WorkerThread + private void rollbackHighImpact(List<RollbackInfo> availableRollbacks, + @FailureReasons int rollbackReason) { + assertInWorkerThread(); + List<RollbackInfo> highImpactRollbacks = + getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_HIGH); + + // sort rollbacks based on package name of the first package. This is to have a + // deterministic order of rollbacks. + List<RollbackInfo> sortedHighImpactRollbacks = highImpactRollbacks.stream().sorted( + Comparator.comparing(a -> a.getPackages().get(0).getPackageName())).toList(); + VersionedPackage firstRollback = + sortedHighImpactRollbacks + .get(0) + .getPackages() + .get(0) + .getVersionRolledBackFrom(); + Slog.i(TAG, "Rolling back high impact rollback for package: " + + firstRollback.getPackageName()); + rollbackPackage(sortedHighImpactRollbacks.get(0), firstRollback, rollbackReason); + } + + @WorkerThread + private void rollbackAll(@FailureReasons int rollbackReason) { + assertInWorkerThread(); + RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); + List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks(); + if (useTwoPhaseRollback(rollbacks)) { + return; + } + + Slog.i(TAG, "Rolling back all available rollbacks"); + // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all + // pending staged rollbacks are handled. + for (RollbackInfo rollback : rollbacks) { + if (rollback.isStaged()) { + mPendingStagedRollbackIds.add(rollback.getRollbackId()); + } + } + + for (RollbackInfo rollback : rollbacks) { + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, rollbackReason); + } + } + + /** + * Rollback all available low impact rollbacks + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollbacks + */ + @WorkerThread + private void rollbackAllLowImpact( + List<RollbackInfo> availableRollbacks, @FailureReasons int rollbackReason) { + assertInWorkerThread(); + + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + if (useTwoPhaseRollback(lowImpactRollbacks)) { + return; + } + + Slog.i(TAG, "Rolling back all available low impact rollbacks"); + logCrashRecoveryEvent(Log.DEBUG, "Rolling back all available. Reason: " + rollbackReason); + // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all + // pending staged rollbacks are handled. + for (RollbackInfo rollback : lowImpactRollbacks) { + if (rollback.isStaged()) { + mPendingStagedRollbackIds.add(rollback.getRollbackId()); + } + } + + for (RollbackInfo rollback : lowImpactRollbacks) { + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, rollbackReason); + } + } + + private List<RollbackInfo> getRollbacksAvailableForImpactLevel( + List<RollbackInfo> availableRollbacks, int impactLevel) { + return availableRollbacks.stream() + .filter(rollbackInfo -> rollbackInfo.getRollbackImpactLevel() == impactLevel) + .toList(); + } + + private int getMinRollbackImpactLevel(List<RollbackInfo> availableRollbacks) { + return availableRollbacks.stream() + .mapToInt(RollbackInfo::getRollbackImpactLevel) + .min() + .orElse(-1); + } + + private int getUserImpactBasedOnRollbackImpactLevel(List<RollbackInfo> availableRollbacks) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + int minImpact = getMinRollbackImpactLevel(availableRollbacks); + switch (minImpact) { + case PackageManager.ROLLBACK_USER_IMPACT_LOW: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + break; + case PackageManager.ROLLBACK_USER_IMPACT_HIGH: + if (!SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90; + } + break; + default: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + return impact; + } + + @VisibleForTesting + Handler getHandler() { + return mHandler; + } +} diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/module/java/com/android/server/rollback/WatchdogRollbackLogger.java index 9cfed02f9355..9cfed02f9355 100644 --- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/rollback/WatchdogRollbackLogger.java diff --git a/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java new file mode 100644 index 000000000000..0b7b98603419 --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/util/ArrayUtils.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.io.File; +import java.util.List; +import java.util.Objects; + +/** + * Copied over from frameworks/base/core/java/com/android/internal/util/ArrayUtils.java + * + * @hide + */ +public class ArrayUtils { + private ArrayUtils() { /* cannot be instantiated */ } + public static final File[] EMPTY_FILE = new File[0]; + + + /** + * Return first index of {@code value} in {@code array}, or {@code -1} if + * not found. + */ + public static <T> int indexOf(@Nullable T[] array, T value) { + if (array == null) return -1; + for (int i = 0; i < array.length; i++) { + if (Objects.equals(array[i], value)) return i; + } + return -1; + } + + /** @hide */ + public static @NonNull File[] defeatNullable(@Nullable File[] val) { + return (val != null) ? val : EMPTY_FILE; + } + + /** + * Checks if given array is null or has zero elements. + */ + public static boolean isEmpty(@Nullable int[] array) { + return array == null || array.length == 0; + } + + /** + * True if the byte array is null or has length 0. + */ + public static boolean isEmpty(@Nullable byte[] array) { + return array == null || array.length == 0; + } + + /** + * Converts from List of bytes to byte array + * @param list + * @return byte[] + */ + public static byte[] toPrimitive(List<byte[]> list) { + if (list.size() == 0) { + return new byte[0]; + } + int byteLen = list.get(0).length; + byte[] array = new byte[list.size() * byteLen]; + for (int i = 0; i < list.size(); i++) { + for (int j = 0; j < list.get(i).length; j++) { + array[i * byteLen + j] = list.get(i)[j]; + } + } + return array; + } + + /** + * Adds value to given array if not already present, providing set-like + * behavior. + */ + public static @NonNull int[] appendInt(@Nullable int[] cur, int val) { + return appendInt(cur, val, false); + } + + /** + * Adds value to given array. + */ + public static @NonNull int[] appendInt(@Nullable int[] cur, int val, + boolean allowDuplicates) { + if (cur == null) { + return new int[] { val }; + } + final int n = cur.length; + if (!allowDuplicates) { + for (int i = 0; i < n; i++) { + if (cur[i] == val) { + return cur; + } + } + } + int[] ret = new int[n + 1]; + System.arraycopy(cur, 0, ret, 0, n); + ret[n] = val; + return ret; + } +} diff --git a/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java new file mode 100644 index 000000000000..9c73feeffb6c --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/util/FileUtils.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Bits and pieces copied from hidden API of android.os.FileUtils. + * + * @hide + */ +public class FileUtils { + /** + * Read a text file into a String, optionally limiting the length. + * + * @param file to read (will not seek, so things like /proc files are OK) + * @param max length (positive for head, negative of tail, 0 for no limit) + * @param ellipsis to add of the file was truncated (can be null) + * @return the contents of the file, possibly truncated + * @throws IOException if something goes wrong reading the file + * @hide + */ + public static @Nullable String readTextFile(@Nullable File file, @Nullable int max, + @Nullable String ellipsis) throws IOException { + InputStream input = new FileInputStream(file); + // wrapping a BufferedInputStream around it because when reading /proc with unbuffered + // input stream, bytes read not equal to buffer size is not necessarily the correct + // indication for EOF; but it is true for BufferedInputStream due to its implementation. + BufferedInputStream bis = new BufferedInputStream(input); + try { + long size = file.length(); + if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes + if (size > 0 && (max == 0 || size < max)) max = (int) size; + byte[] data = new byte[max + 1]; + int length = bis.read(data); + if (length <= 0) return ""; + if (length <= max) return new String(data, 0, length); + if (ellipsis == null) return new String(data, 0, max); + return new String(data, 0, max) + ellipsis; + } else if (max < 0) { // "tail" mode: keep the last N + int len; + boolean rolled = false; + byte[] last = null; + byte[] data = null; + do { + if (last != null) rolled = true; + byte[] tmp = last; + last = data; + data = tmp; + if (data == null) data = new byte[-max]; + len = bis.read(data); + } while (len == data.length); + + if (last == null && len <= 0) return ""; + if (last == null) return new String(data, 0, len); + if (len > 0) { + rolled = true; + System.arraycopy(last, len, last, 0, last.length - len); + System.arraycopy(data, 0, last, last.length - len, len); + } + if (ellipsis == null || !rolled) return new String(last); + return ellipsis + new String(last); + } else { // "cat" mode: size unknown, read it all in streaming fashion + ByteArrayOutputStream contents = new ByteArrayOutputStream(); + int len; + byte[] data = new byte[1024]; + do { + len = bis.read(data); + if (len > 0) contents.write(data, 0, len); + } while (len == data.length); + return contents.toString(); + } + } finally { + bis.close(); + input.close(); + } + } + + /** + * Perform an fsync on the given FileOutputStream. The stream at this + * point must be flushed but not yet closed. + * + * @hide + */ + public static boolean sync(FileOutputStream stream) { + try { + if (stream != null) { + stream.getFD().sync(); + } + return true; + } catch (IOException e) { + } + return false; + } + + /** + * List the files in the directory or return empty file. + * + * @hide + */ + public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) { + return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles()) + : ArrayUtils.EMPTY_FILE; + } +} diff --git a/packages/CrashRecovery/services/module/java/com/android/util/LongArrayQueue.java b/packages/CrashRecovery/services/module/java/com/android/util/LongArrayQueue.java new file mode 100644 index 000000000000..9a24ada8b69a --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/util/LongArrayQueue.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import libcore.util.EmptyArray; + +import java.util.NoSuchElementException; + +/** + * Copied from frameworks/base/core/java/android/util/LongArrayQueue.java + * + * @hide + */ +public class LongArrayQueue { + + private long[] mValues; + private int mSize; + private int mHead; + private int mTail; + + private long[] newUnpaddedLongArray(int num) { + return new long[num]; + } + /** + * Initializes a queue with the given starting capacity. + * + * @param initialCapacity the capacity. + */ + public LongArrayQueue(int initialCapacity) { + if (initialCapacity == 0) { + mValues = EmptyArray.LONG; + } else { + mValues = newUnpaddedLongArray(initialCapacity); + } + mSize = 0; + mHead = mTail = 0; + } + + /** + * Initializes a queue with default starting capacity. + */ + public LongArrayQueue() { + this(16); + } + + /** @hide */ + public static int growSize(int currentSize) { + return currentSize <= 4 ? 8 : currentSize * 2; + } + + private void grow() { + if (mSize < mValues.length) { + throw new IllegalStateException("Queue not full yet!"); + } + final int newSize = growSize(mSize); + final long[] newArray = newUnpaddedLongArray(newSize); + final int r = mValues.length - mHead; // Number of elements on and to the right of head. + System.arraycopy(mValues, mHead, newArray, 0, r); + System.arraycopy(mValues, 0, newArray, r, mHead); + mValues = newArray; + mHead = 0; + mTail = mSize; + } + + /** + * Returns the number of elements in the queue. + */ + public int size() { + return mSize; + } + + /** + * Removes all elements from this queue. + */ + public void clear() { + mSize = 0; + mHead = mTail = 0; + } + + /** + * Adds a value to the tail of the queue. + * + * @param value the value to be added. + */ + public void addLast(long value) { + if (mSize == mValues.length) { + grow(); + } + mValues[mTail] = value; + mTail = (mTail + 1) % mValues.length; + mSize++; + } + + /** + * Removes an element from the head of the queue. + * + * @return the element at the head of the queue. + * @throws NoSuchElementException if the queue is empty. + */ + public long removeFirst() { + if (mSize == 0) { + throw new NoSuchElementException("Queue is empty!"); + } + final long ret = mValues[mHead]; + mHead = (mHead + 1) % mValues.length; + mSize--; + return ret; + } + + /** + * Returns the element at the given position from the head of the queue, where 0 represents the + * head of the queue. + * + * @param position the position from the head of the queue. + * @return the element found at the given position. + * @throws IndexOutOfBoundsException if {@code position} < {@code 0} or + * {@code position} >= {@link #size()} + */ + public long get(int position) { + if (position < 0 || position >= mSize) { + throw new IndexOutOfBoundsException("Index " + position + + " not valid for a queue of size " + mSize); + } + final int index = (mHead + position) % mValues.length; + return mValues[index]; + } + + /** + * Returns the element at the head of the queue, without removing it. + * + * @return the element at the head of the queue. + * @throws NoSuchElementException if the queue is empty + */ + public long peekFirst() { + if (mSize == 0) { + throw new NoSuchElementException("Queue is empty!"); + } + return mValues[mHead]; + } + + /** + * Returns the element at the tail of the queue. + * + * @return the element at the tail of the queue. + * @throws NoSuchElementException if the queue is empty. + */ + public long peekLast() { + if (mSize == 0) { + throw new NoSuchElementException("Queue is empty!"); + } + final int index = (mTail == 0) ? mValues.length - 1 : mTail - 1; + return mValues[index]; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + if (mSize <= 0) { + return "{}"; + } + + final StringBuilder buffer = new StringBuilder(mSize * 64); + buffer.append('{'); + buffer.append(get(0)); + for (int i = 1; i < mSize; i++) { + buffer.append(", "); + buffer.append(get(i)); + } + buffer.append('}'); + return buffer.toString(); + } +} diff --git a/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java new file mode 100644 index 000000000000..50823f5c9c34 --- /dev/null +++ b/packages/CrashRecovery/services/module/java/com/android/util/XmlUtils.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import android.annotation.NonNull; +import android.system.ErrnoException; +import android.system.Os; + +import com.android.modules.utils.TypedXmlPullParser; + +import libcore.util.XmlObjectFactory; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Bits and pieces copied from hidden API of + * frameworks/base/core/java/com/android/internal/util/XmlUtils.java + * + * @hide + */ +public class XmlUtils { + + private static final String STRING_ARRAY_SEPARATOR = ":"; + + /** @hide */ + public static final void beginDocument(XmlPullParser parser, String firstElementName) + throws XmlPullParserException, IOException { + int type; + while ((type = parser.next()) != parser.START_TAG + && type != parser.END_DOCUMENT) { + // Do nothing + } + + if (type != parser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + if (!parser.getName().equals(firstElementName)) { + throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + + ", expected " + firstElementName); + } + } + + /** @hide */ + public static boolean nextElementWithin(XmlPullParser parser, int outerDepth) + throws IOException, XmlPullParserException { + for (;;) { + int type = parser.next(); + if (type == XmlPullParser.END_DOCUMENT + || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) { + return false; + } + if (type == XmlPullParser.START_TAG + && parser.getDepth() == outerDepth + 1) { + return true; + } + } + } + + private static XmlPullParser newPullParser() { + try { + XmlPullParser parser = XmlObjectFactory.newXmlPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + return parser; + } catch (XmlPullParserException e) { + throw new AssertionError(); + } + } + + /** @hide */ + public static @NonNull TypedXmlPullParser resolvePullParser(@NonNull InputStream in) + throws IOException { + final byte[] magic = new byte[4]; + if (in instanceof FileInputStream) { + try { + Os.pread(((FileInputStream) in).getFD(), magic, 0, magic.length, 0); + } catch (ErrnoException e) { + throw e.rethrowAsIOException(); + } + } else { + if (!in.markSupported()) { + in = new BufferedInputStream(in); + } + in.mark(8); + in.read(magic); + in.reset(); + } + + final TypedXmlPullParser xml; + xml = (TypedXmlPullParser) newPullParser(); + try { + xml.setInput(in, "UTF_8"); + } catch (XmlPullParserException e) { + throw new IOException(e); + } + return xml; + } +} diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/packages/CrashRecovery/services/platform/java/com/android/server/ExplicitHealthCheckController.java index 6a6aea49f6f2..6a6aea49f6f2 100644 --- a/services/core/java/com/android/server/ExplicitHealthCheckController.java +++ b/packages/CrashRecovery/services/platform/java/com/android/server/ExplicitHealthCheckController.java diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java index 2acedd56e505..2acedd56e505 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java diff --git a/services/core/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java index feb5775e5aac..feb5775e5aac 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/packages/CrashRecovery/services/platform/java/com/android/server/RescueParty.java diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryModule.java b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryModule.java new file mode 100644 index 000000000000..8a81aaa1e636 --- /dev/null +++ b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryModule.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.crashrecovery; + +import android.content.Context; + +import com.android.server.PackageWatchdog; +import com.android.server.RescueParty; +import com.android.server.SystemService; + + +/** This class encapsulate the lifecycle methods of CrashRecovery module. + * + * @hide + */ +public class CrashRecoveryModule { + private static final String TAG = "CrashRecoveryModule"; + + /** Lifecycle definition for CrashRecovery module. */ + public static class Lifecycle extends SystemService { + private Context mSystemContext; + private PackageWatchdog mPackageWatchdog; + + public Lifecycle(Context context) { + super(context); + mSystemContext = context; + mPackageWatchdog = PackageWatchdog.getInstance(context); + } + + @Override + public void onStart() { + RescueParty.registerHealthObserver(mSystemContext); + mPackageWatchdog.registerShutdownBroadcastReceiver(); + mPackageWatchdog.noteBoot(); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + mPackageWatchdog.onPackagesReady(); + } + } + } +} diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryUtils.java b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryUtils.java new file mode 100644 index 000000000000..2e2a93776f9d --- /dev/null +++ b/packages/CrashRecovery/services/platform/java/com/android/server/crashrecovery/CrashRecoveryUtils.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.crashrecovery; + +import android.os.Environment; +import android.util.IndentingPrintWriter; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintWriter; +import java.time.LocalDateTime; +import java.time.ZoneId; + +/** + * Class containing helper methods for the CrashRecoveryModule. + * + * @hide + */ +public class CrashRecoveryUtils { + private static final String TAG = "CrashRecoveryUtils"; + private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 1000 * 1000; // ~1MB + private static final Object sFileLock = new Object(); + + /** Persist recovery related events in crashrecovery events file.**/ + public static void logCrashRecoveryEvent(int priority, String msg) { + Log.println(priority, TAG, msg); + try { + File fname = getCrashRecoveryEventsFile(); + synchronized (sFileLock) { + FileOutputStream out = new FileOutputStream(fname, true); + PrintWriter pw = new PrintWriter(out); + String dateString = LocalDateTime.now(ZoneId.systemDefault()).toString(); + pw.println(dateString + ": " + msg); + pw.close(); + } + } catch (IOException e) { + Log.e(TAG, "Unable to log CrashRecoveryEvents " + e.getMessage()); + } + } + + /** Dump recovery related events from crashrecovery events file.**/ + public static void dumpCrashRecoveryEvents(IndentingPrintWriter pw) { + pw.println("CrashRecovery Events: "); + pw.increaseIndent(); + final File file = getCrashRecoveryEventsFile(); + final long skipSize = file.length() - MAX_CRITICAL_INFO_DUMP_SIZE; + synchronized (sFileLock) { + try (BufferedReader in = new BufferedReader(new FileReader(file))) { + if (skipSize > 0) { + in.skip(skipSize); + } + String line; + while ((line = in.readLine()) != null) { + pw.println(line); + } + } catch (IOException e) { + Log.e(TAG, "Unable to dump CrashRecoveryEvents " + e.getMessage()); + } + } + pw.decreaseIndent(); + } + + private static File getCrashRecoveryEventsFile() { + File systemDir = new File(Environment.getDataDirectory(), "system"); + return new File(systemDir, "crashrecovery-events.txt"); + } +} diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java index d206c66ed09a..d206c66ed09a 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/RollbackPackageHealthObserver.java diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/WatchdogRollbackLogger.java new file mode 100644 index 000000000000..9cfed02f9355 --- /dev/null +++ b/packages/CrashRecovery/services/platform/java/com/android/server/rollback/WatchdogRollbackLogger.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.rollback; + +import static com.android.server.crashrecovery.CrashRecoveryUtils.logCrashRecoveryEvent; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.content.pm.VersionedPackage; +import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; +import android.os.SystemProperties; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.PackageWatchdog; +import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; + +import java.util.List; + +/** + * This class handles the logic for logging Watchdog-triggered rollback events. + * @hide + */ +public final class WatchdogRollbackLogger { + private static final String TAG = "WatchdogRollbackLogger"; + + private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT"; + + private WatchdogRollbackLogger() { + } + + @Nullable + private static String getLoggingParentName(Context context, @NonNull String packageName) { + PackageManager packageManager = context.getPackageManager(); + try { + int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA; + ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo; + if (ai.metaData == null) { + return null; + } + return ai.metaData.getString(LOGGING_PARENT_KEY); + } catch (Exception e) { + Slog.w(TAG, "Unable to discover logging parent package: " + packageName, e); + return null; + } + } + + /** + * Returns the logging parent of a given package if it exists, {@code null} otherwise. + * + * The logging parent is defined by the {@code android.content.pm.LOGGING_PARENT} field in the + * metadata of a package's AndroidManifest.xml. + */ + @VisibleForTesting + @Nullable + static VersionedPackage getLogPackage(Context context, + @NonNull VersionedPackage failingPackage) { + String logPackageName; + VersionedPackage loggingParent; + logPackageName = getLoggingParentName(context, failingPackage.getPackageName()); + if (logPackageName == null) { + return null; + } + try { + loggingParent = new VersionedPackage(logPackageName, context.getPackageManager() + .getPackageInfo(logPackageName, 0 /* flags */).getLongVersionCode()); + } catch (PackageManager.NameNotFoundException e) { + return null; + } + return loggingParent; + } + + static void logRollbackStatusOnBoot(Context context, int rollbackId, String logPackageName, + List<RollbackInfo> recentlyCommittedRollbacks) { + PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); + + RollbackInfo rollback = null; + for (RollbackInfo info : recentlyCommittedRollbacks) { + if (rollbackId == info.getRollbackId()) { + rollback = info; + break; + } + } + + if (rollback == null) { + Slog.e(TAG, "rollback info not found for last staged rollback: " + rollbackId); + return; + } + + // Use the version of the logging parent that was installed before + // we rolled back for logging purposes. + VersionedPackage oldLoggingPackage = null; + if (!TextUtils.isEmpty(logPackageName)) { + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + if (logPackageName.equals(packageRollback.getPackageName())) { + oldLoggingPackage = packageRollback.getVersionRolledBackFrom(); + break; + } + } + } + + int sessionId = rollback.getCommittedSessionId(); + PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); + if (sessionInfo == null) { + Slog.e(TAG, "On boot completed, could not load session id " + sessionId); + return; + } + + if (sessionInfo.isStagedSessionApplied()) { + logEvent(oldLoggingPackage, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); + } else if (sessionInfo.isStagedSessionFailed()) { + logEvent(oldLoggingPackage, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE, + WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, ""); + } + } + + /** + * Log a Watchdog rollback event to statsd. + * + * @param logPackage the package to associate the rollback with. + * @param type the state of the rollback. + * @param rollbackReason the reason Watchdog triggered a rollback, if known. + * @param failingPackageName the failing package or process which triggered the rollback. + */ + public static void logEvent(@Nullable VersionedPackage logPackage, int type, + int rollbackReason, @NonNull String failingPackageName) { + String logMsg = "Watchdog event occurred with type: " + rollbackTypeToString(type) + + " logPackage: " + logPackage + + " rollbackReason: " + rollbackReasonToString(rollbackReason) + + " failedPackageName: " + failingPackageName; + Slog.i(TAG, logMsg); + if (logPackage != null) { + CrashRecoveryStatsLog.write( + CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED, + type, + logPackage.getPackageName(), + logPackage.getVersionCode(), + rollbackReason, + failingPackageName, + new byte[]{}); + } else { + // In the case that the log package is null, still log an empty string as an + // indication that retrieving the logging parent failed. + CrashRecoveryStatsLog.write( + CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED, + type, + "", + 0, + rollbackReason, + failingPackageName, + new byte[]{}); + } + + logTestProperties(logMsg); + } + + /** + * Writes properties which will be used by rollback tests to check if particular rollback + * events have occurred. + */ + private static void logTestProperties(String logMsg) { + // This property should be on only during the tests + if (!SystemProperties.getBoolean("persist.sys.rollbacktest.enabled", false)) { + return; + } + logCrashRecoveryEvent(Log.DEBUG, logMsg); + } + + @VisibleForTesting + static int mapFailureReasonToMetric(@PackageWatchdog.FailureReasons int failureReason) { + switch (failureReason) { + case PackageWatchdog.FAILURE_REASON_NATIVE_CRASH: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; + case PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; + case PackageWatchdog.FAILURE_REASON_APP_CRASH: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; + case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; + case PackageWatchdog.FAILURE_REASON_BOOT_LOOP: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING; + default: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; + } + } + + private static String rollbackTypeToString(int type) { + switch (type) { + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE: + return "ROLLBACK_INITIATE"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS: + return "ROLLBACK_SUCCESS"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE: + return "ROLLBACK_FAILURE"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED: + return "ROLLBACK_BOOT_TRIGGERED"; + default: + return "UNKNOWN"; + } + } + + private static String rollbackReasonToString(int reason) { + switch (reason) { + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH: + return "REASON_NATIVE_CRASH"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK: + return "REASON_EXPLICIT_HEALTH_CHECK"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH: + return "REASON_APP_CRASH"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING: + return "REASON_APP_NOT_RESPONDING"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT: + return "REASON_NATIVE_CRASH_DURING_BOOT"; + case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING: + return "REASON_BOOT_LOOP"; + default: + return "UNKNOWN"; + } + } +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index d688a1a036d1..824dd4a5fdaf 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -44,7 +44,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Process; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -101,8 +100,7 @@ public class PackageInstallerActivity extends Activity { private int mActivityResultCode = Activity.RESULT_CANCELED; private int mPendingUserActionReason = -1; - private final boolean mLocalLOGV = - TextUtils.equals("userdebug", SystemProperties.get("ro.build.type", "")); + private final boolean mLocalLOGV = false; PackageManager mPm; AppOpsManager mAppOpsManager; UserManager mUserManager; @@ -145,11 +143,6 @@ public class PackageInstallerActivity extends Activity { private AlertDialog mDialog; private void startInstallConfirm() { - if (mLocalLOGV) { - Log.d(TAG, "startInstallConfirm mAppInfo = " + mAppInfo - + ", existingUpdateOwner = " + getExistingUpdateOwner() - + ", mOriginatingPackage = " + mOriginatingPackage); - } TextView viewToEnable; if (mAppInfo != null) { @@ -190,10 +183,6 @@ public class PackageInstallerActivity extends Activity { try { final String packageName = mPkgInfo.packageName; final InstallSourceInfo sourceInfo = mPm.getInstallSourceInfo(packageName); - if (mLocalLOGV) { - Log.d(TAG, "getExistingUpdateOwner mAppInfo = " + mAppInfo - + ", packageName = " + packageName + ", sourceInfo = " + sourceInfo); - } return sourceInfo.getUpdateOwnerPackageName(); } catch (NameNotFoundException e) { return null; @@ -314,12 +303,6 @@ public class PackageInstallerActivity extends Activity { private void initiateInstall() { final String existingUpdateOwner = getExistingUpdateOwner(); - if (mLocalLOGV) { - Log.d(TAG, "initiateInstall mAppInfo = " + mAppInfo - + ", existingUpdateOwner = " + existingUpdateOwner - + ", mOriginatingPackage = " + mOriginatingPackage - + ", mSessionId = " + mSessionId); - } if (mSessionId == SessionInfo.INVALID_ID && !TextUtils.isEmpty(existingUpdateOwner) && !TextUtils.equals(existingUpdateOwner, mOriginatingPackage)) { @@ -831,28 +814,15 @@ public class PackageInstallerActivity extends Activity { @Override public void onOpChanged(String op, String packageName) { - if (mLocalLOGV) { - Log.d(TAG, "UnknownSourcesListener onOpChanged op = " + op - + ", packageName = " + packageName - + ", mOriginatingPackage = " + mOriginatingPackage); - } if (!mOriginatingPackage.equals(packageName)) { return; } unregister(this); mActiveUnknownSourcesListeners.remove(this); - if (mLocalLOGV) { - Log.d(TAG, "UnknownSourcesListener onOpChanged isDestroyed() = " - + isDestroyed()); - } if (isDestroyed()) { return; } new Handler(Looper.getMainLooper()).postDelayed(() -> { - if (mLocalLOGV) { - Log.d(TAG, "UnknownSourcesListener onOpChanged post isDestroyed()" - + "= " + isDestroyed() + ", getIntent() = " + getIntent()); - } if (!isDestroyed()) { startActivity(getIntent()); // The start flag (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP) doesn't @@ -870,9 +840,6 @@ public class PackageInstallerActivity extends Activity { } private void register(UnknownSourcesListener listener) { - if (mLocalLOGV) { - Log.d(TAG, "UnknownSourcesListener register"); - } mAppOpsManager.startWatchingMode( AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, mOriginatingPackage, listener); @@ -880,9 +847,6 @@ public class PackageInstallerActivity extends Activity { } private void unregister(UnknownSourcesListener listener) { - if (mLocalLOGV) { - Log.d(TAG, "UnknownSourcesListener unregister"); - } mAppOpsManager.stopWatchingMode(listener); mActiveUnknownSourcesListeners.remove(listener); } diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java index b2861826a103..601e001f48c2 100644 --- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -57,7 +57,7 @@ import java.util.List; * 1. User sets invisible for button. ex: ActionButtonPreference.setButton1Visible(false) * 2. User doesn't set any title or icon for button. */ -public class ActionButtonsPreference extends Preference { +public class ActionButtonsPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "ActionButtonPreference"; private static final boolean mIsAtLeastS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S; diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 6cd777e878fe..10769ecfbe42 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -43,7 +43,7 @@ import com.android.settingslib.widget.preference.banner.R; * Banner message is a banner displaying important information (permission request, page error etc), * and provide actions for user to address. It requires a user action to be dismissed. */ -public class BannerMessagePreference extends Preference { +public class BannerMessagePreference extends Preference implements GroupSectionDividerMixin { public enum AttentionLevel { HIGH(0, R.color.banner_background_attention_high, R.color.banner_accent_attention_high), diff --git a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt index eb14746a0f22..84ff1bbef65d 100644 --- a/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt +++ b/packages/SettingsLib/CardPreference/src/com/android/settingslib/widget/CardPreference.kt @@ -30,7 +30,7 @@ class CardPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { init { layoutResource = R.layout.settingslib_expressive_preference_card diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt index 8c5d8778c96f..8fb16d8ac840 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt @@ -78,6 +78,10 @@ private constructor(private val context: Context, private val request: GetPrefer for (activityClass in request.activityClasses) { add(activityClass) } + // Temporarily add all screens + for (key in PreferenceScreenRegistry.preferenceScreens.keys) { + addPreferenceScreenFromRegistry(key, Activity::class.java) + } } fun build() = builder.build() diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp index cd8f584953aa..12890feaf56e 100644 --- a/packages/SettingsLib/IllustrationPreference/Android.bp +++ b/packages/SettingsLib/IllustrationPreference/Android.bp @@ -21,6 +21,7 @@ android_library { "SettingsLibColor", "androidx.preference_preference", "lottie", + "SettingsLibSettingsTheme", "settingslib_illustrationpreference_flags_lib", ], diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java index a0599bb32dd1..adc4f316aca9 100644 --- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java +++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java @@ -55,7 +55,7 @@ import java.io.InputStream; /** * IllustrationPreference is a preference that can play lottie format animation */ -public class IllustrationPreference extends Preference { +public class IllustrationPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "IllustrationPreference"; diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml index e3f8fbb88a65..2e3ee32e2efb 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index 255b2c92e709..3e0e18488f36 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingTop="@dimen/settingslib_switchbar_margin" android:paddingBottom="@dimen/settingslib_switchbar_margin" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml index 4425ef08d6e3..f75d9b23c49b 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v35/settingslib_expressive_main_switch_bar.xml @@ -20,8 +20,6 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingVertical="@dimen/settingslib_expressive_space_small1" android:orientation="vertical"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml index bf34db93298b..7c0eaeaca3de 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml @@ -18,11 +18,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" - android:layout_width="match_parent" - android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingRight="?android:attr/listPreferredItemPaddingRight" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> + android:layout_width="match_parent"> <TextView android:id="@+id/switch_text" diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml index bef6e352d854..fa908a4ed6c8 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_layout.xml @@ -18,6 +18,10 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" + android:paddingLeft="?android:attr/listPreferredItemPaddingLeft" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingRight="?android:attr/listPreferredItemPaddingRight" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:importantForAccessibility="no"> <com.android.settingslib.widget.MainSwitchBar diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java index d895c874d95e..3394874797e3 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchPreference.java @@ -36,7 +36,8 @@ import java.util.List; * This component is used as the main switch of the page * to enable or disable the prefereces on the page. */ -public class MainSwitchPreference extends TwoStatePreference implements OnCheckedChangeListener { +public class MainSwitchPreference extends TwoStatePreference + implements OnCheckedChangeListener, GroupSectionDividerMixin { private final List<OnCheckedChangeListener> mSwitchChangeListeners = new ArrayList<>(); diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt index f39f3a065e79..33f2dbf73cf3 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceMetadata.kt @@ -31,6 +31,7 @@ import androidx.annotation.StringRes * information: * - [PreferenceTitleProvider]: provide dynamic title content * - [PreferenceSummaryProvider]: provide dynamic summary content (e.g. based on preference value) + * - [PreferenceIconProvider]: provide dynamic icon content (e.g. based on flag) * - [PreferenceAvailabilityProvider]: provide preference availability (e.g. based on flag) * - [PreferenceLifecycleProvider]: provide the lifecycle callbacks and notify state change * @@ -160,6 +161,19 @@ interface PreferenceMetadata { this is PreferenceSummaryProvider -> getSummary(context) else -> null } + + /** + * Returns the preference icon. + * + * Implement [PreferenceIconProvider] interface if icon content is provided dynamically + * (e.g. icon is provided based on flag value). + */ + fun getPreferenceIcon(context: Context): Int = + when { + icon != 0 -> icon + this is PreferenceIconProvider -> getIcon(context) + else -> 0 + } } /** Metadata of preference group. */ diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt index 48798da57dae..6646d6c32d15 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt @@ -34,7 +34,7 @@ object PreferenceScreenRegistry : ReadWritePermitProvider { ImmutableMap.of() } - private val preferenceScreens: PreferenceScreenMap + val preferenceScreens: PreferenceScreenMap get() = preferenceScreensSupplier.get() private var readWritePermitProvider: ReadWritePermitProvider? = null 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 a3aa85df5325..98cba1cf098a 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt @@ -41,6 +41,17 @@ interface PreferenceSummaryProvider { } /** + * Interface to provide dynamic preference icon. + * + * Implement this interface implies that the preference icon should not be cached for indexing. + */ +interface PreferenceIconProvider { + + /** Provides preference icon. */ + fun getIcon(context: Context): Int +} + +/** * Interface to provide the state of preference availability. * * UI framework normally does not show the preference widget if it is unavailable. diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt index 5e6989546cb9..ef3d372a4088 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt @@ -62,12 +62,13 @@ interface PreferenceBinding { fun bind(preference: Preference, metadata: PreferenceMetadata) { metadata.apply { preference.key = key - if (icon != 0) { - preference.setIcon(icon) + val context = preference.context + val preferenceIcon = metadata.getPreferenceIcon(context) + if (preferenceIcon != 0) { + preference.setIcon(preferenceIcon) } else { preference.icon = null } - val context = preference.context val isPreferenceScreen = preference is PreferenceScreen preference.peekExtras()?.clear() extras(context)?.let { preference.extras.putAll(it) } diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt index debaf3e2338b..d501f4fd654d 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt @@ -41,6 +41,9 @@ open class PreferenceFragment : createPreferenceScreen(PreferenceScreenFactory(this)) override fun createPreferenceScreen(factory: PreferenceScreenFactory): PreferenceScreen? { + preferenceScreenBindingHelper?.close() + preferenceScreenBindingHelper = null + val context = factory.context fun createPreferenceScreenFromResource() = factory.inflate(getPreferenceScreenResId(context))?.also { @@ -86,9 +89,13 @@ open class PreferenceFragment : override fun onDestroy() { preferenceScreenBindingHelper?.close() + preferenceScreenBindingHelper = null super.onDestroy() } + protected fun getPreferenceKeysInHierarchy(): Set<String> = + preferenceScreenBindingHelper?.getPreferences()?.map { it.key }?.toSet() ?: setOf() + companion object { private const val TAG = "PreferenceFragment" } 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 3610894c3fc0..95b921b8e8c8 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt @@ -45,7 +45,7 @@ class PreferenceScreenBindingHelper( context: Context, private val preferenceBindingFactory: PreferenceBindingFactory, private val preferenceScreen: PreferenceScreen, - preferenceHierarchy: PreferenceHierarchy, + private val preferenceHierarchy: PreferenceHierarchy, ) : KeyedDataObservable<String>(), AutoCloseable { private val handler = Handler(Looper.getMainLooper()) @@ -133,6 +133,8 @@ class PreferenceScreenBindingHelper( } } + fun getPreferences() = preferenceHierarchy.getAllPreferences() + override fun close() { removeObserver(preferenceObserver) val context = preferenceScreen.context diff --git a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt index 4d5f85fa9020..e27838c4ef4d 100644 --- a/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt +++ b/packages/SettingsLib/Preference/testutils/com/android/settingslib/preference/CatalystScreenTestCase.kt @@ -49,15 +49,15 @@ abstract class CatalystScreenTestCase { * catalyst screen (flag is enabled). */ @Test - fun migration() { + open fun migration() { enableCatalystScreen() assertThat(preferenceScreenCreator.isFlagEnabled(context)).isTrue() - val catalystScreen = stringifyPreferenceScreen() + val catalystScreen = dumpPreferenceScreen() Log.i("Catalyst", catalystScreen) disableCatalystScreen() assertThat(preferenceScreenCreator.isFlagEnabled(context)).isFalse() - val legacyScreen = stringifyPreferenceScreen() + val legacyScreen = dumpPreferenceScreen() assertThat(catalystScreen).isEqualTo(legacyScreen) } @@ -82,7 +82,7 @@ abstract class CatalystScreenTestCase { setFlagsRule.disableFlags(flagName) } - private fun stringifyPreferenceScreen(): String { + private fun dumpPreferenceScreen(): String { @Suppress("UNCHECKED_CAST") val clazz = preferenceScreenCreator.fragmentClass() as Class<PreferenceFragmentCompat> val builder = StringBuilder() diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt new file mode 100644 index 000000000000..ba5f5cf855be --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/GroupSectionDividerMixin.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +/** + * A base interface to indicate that a class should not have rounded corners. + * + * Classes implementing this interface will be treated as already handle rounded corners. + */ +interface GroupSectionDividerMixin
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt new file mode 100644 index 000000000000..535d80f609fb --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsBasePreferenceFragment.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceScreen +import androidx.recyclerview.widget.RecyclerView + +/** Base class for Settings to use PreferenceFragmentCompat */ +open abstract class SettingsBasePreferenceFragment : PreferenceFragmentCompat() { + + override fun onCreateAdapter(preferenceScreen: PreferenceScreen): RecyclerView.Adapter<*> { + if (SettingsThemeHelper.isExpressiveTheme(requireContext())) + return SettingsPreferenceGroupAdapter(preferenceScreen) + return super.onCreateAdapter(preferenceScreen) + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt new file mode 100644 index 000000000000..98b7f7664b1b --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.widget + +import android.os.Handler +import android.os.Looper +import androidx.annotation.DrawableRes +import androidx.preference.Preference +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceGroupAdapter +import androidx.preference.PreferenceViewHolder +import com.android.settingslib.widget.theme.R + +/** + * A custom adapter for displaying settings preferences in a list, handling rounded corners + * for preference items within a group. + */ +open class SettingsPreferenceGroupAdapter @JvmOverloads constructor( + preferenceGroup: PreferenceGroup +) : PreferenceGroupAdapter(preferenceGroup) { + + private val mPreferenceGroup = preferenceGroup + private var mRoundCornerMappingList: ArrayList<Int> = ArrayList() + + private var mNormalPaddingStart = 0 + private var mGroupPaddingStart = 0 + private var mNormalPaddingEnd = 0 + private var mGroupPaddingEnd = 0 + + private val mHandler = Handler(Looper.getMainLooper()) + + private val syncRunnable = Runnable { updatePreferences() } + + init { + val context = preferenceGroup.context + mNormalPaddingStart = + context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1) + mGroupPaddingStart = mNormalPaddingStart * 2 + mNormalPaddingEnd = + context.resources.getDimensionPixelSize(R.dimen.settingslib_expressive_space_small1) + mGroupPaddingEnd = mNormalPaddingEnd * 2 + updatePreferences() + } + + override fun onPreferenceHierarchyChange(preference: Preference) { + super.onPreferenceHierarchyChange(preference) + + // Post after super class has posted their sync runnable to update preferences. + mHandler.removeCallbacks(syncRunnable) + mHandler.post(syncRunnable) + } + + override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { + super.onBindViewHolder(holder, position) + updateBackground(holder, position) + } + + private fun updatePreferences() { + val oldList = ArrayList(mRoundCornerMappingList) + mRoundCornerMappingList = ArrayList() + mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup) + if (mRoundCornerMappingList != oldList) { + notifyDataSetChanged() + } + } + + private fun mappingPreferenceGroup(cornerStyles: MutableList<Int>, group: PreferenceGroup) { + cornerStyles.clear() + cornerStyles.addAll(MutableList(itemCount) { 0 }) + + // the first item in to group + var startIndex = -1 + // the last item in the group + var endIndex = -1 + var currentParent: PreferenceGroup? = group + for (i in 0 until itemCount) { + when (val pref = getItem(i)) { + // the preference has round corner background, so we don't need to handle it. + is GroupSectionDividerMixin -> { + cornerStyles[i] = 0 + startIndex = -1 + endIndex = -1 + } + + // PreferenceCategory should not have round corner background. + is PreferenceCategory -> { + cornerStyles[i] = 0 + startIndex = -1 + endIndex = -1 + currentParent = pref + } + + // ExpandablePreference is PreferenceGroup but it should handle round corner + is Expandable -> { + // When ExpandablePreference is expanded, we treat is as the first item. + if (pref.isExpanded()) { + currentParent = pref as? PreferenceGroup + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP or ROUND_CORNER_CENTER + endIndex = -1 + } + } + + else -> { + val parent = pref?.parent + + // item in the group should have round corner background. + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_CENTER + if (parent === currentParent) { + // find the first item in the group + if (startIndex == -1) { + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP + } + + // find the last item in the group, if we find the new last item, we should + // remove the old last item round corner. + if (endIndex == -1 || endIndex < i) { + if (endIndex != -1) { + cornerStyles[endIndex] = + cornerStyles[endIndex] and ROUND_CORNER_BOTTOM.inv() + } + endIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM + } + } else { + // this item is new group, we should reset the index. + currentParent = parent + startIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_TOP + endIndex = i + cornerStyles[i] = cornerStyles[i] or ROUND_CORNER_BOTTOM + } + } + } + } + } + + /** handle roundCorner background */ + private fun updateBackground(holder: PreferenceViewHolder, position: Int) { + @DrawableRes val backgroundRes = getRoundCornerDrawableRes(position, false /* isSelected*/) + + val v = holder.itemView + val paddingStart = if (backgroundRes == 0) mNormalPaddingStart else mGroupPaddingStart + val paddingEnd = if (backgroundRes == 0) mNormalPaddingEnd else mGroupPaddingEnd + + v.setPaddingRelative(paddingStart, v.paddingTop, paddingEnd, v.paddingBottom) + v.setBackgroundResource(backgroundRes) + } + + @DrawableRes + protected fun getRoundCornerDrawableRes(position: Int, isSelected: Boolean): Int { + val cornerType = mRoundCornerMappingList[position] + + if ((cornerType and ROUND_CORNER_CENTER) == 0) { + return 0 + } + + return when { + (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) == 0 -> { + // the first + if (isSelected) R.drawable.settingslib_round_background_top_selected + else R.drawable.settingslib_round_background_top + } + + (cornerType and ROUND_CORNER_BOTTOM) != 0 && (cornerType and ROUND_CORNER_TOP) == 0 -> { + // the last + if (isSelected) R.drawable.settingslib_round_background_bottom_selected + else R.drawable.settingslib_round_background_bottom + } + + (cornerType and ROUND_CORNER_TOP) != 0 && (cornerType and ROUND_CORNER_BOTTOM) != 0 -> { + // the only one preference + if (isSelected) R.drawable.settingslib_round_background_selected + else R.drawable.settingslib_round_background + } + + else -> { + // in the center + if (isSelected) R.drawable.settingslib_round_background_center_selected + else R.drawable.settingslib_round_background_center + } + } + } + + companion object { + private const val ROUND_CORNER_CENTER: Int = 1 + private const val ROUND_CORNER_TOP: Int = 1 shl 1 + private const val ROUND_CORNER_BOTTOM: Int = 1 shl 2 + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts index b69912a3fd36..73d0beccd0ba 100644 --- a/packages/SettingsLib/Spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -29,7 +29,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath allprojects { extra["androidTop"] = androidTop - extra["jetpackComposeVersion"] = "1.7.0" + extra["jetpackComposeVersion"] = "1.7.3" } subprojects { diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts index 8f8275bed702..914f06c1fef7 100644 --- a/packages/SettingsLib/Spa/spa/build.gradle.kts +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -54,7 +54,7 @@ android { dependencies { api(project(":SettingsLibColor")) api("androidx.appcompat:appcompat:1.7.0") - api("androidx.compose.material3:material3:1.3.0") + api("androidx.compose.material3:material3:1.4.0-alpha01") api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion") api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion") api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt index 2c55779c9a01..693fb3541bb8 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt @@ -46,6 +46,7 @@ import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.Stable import androidx.compose.runtime.derivedStateOf @@ -326,6 +327,9 @@ private fun TwoRowsTopAppBar( // Sets the app bar's height offset limit to hide just the bottom title area and keep top title // visible when collapsed. scrollBehavior?.state?.heightOffsetLimit = pinnedHeightPx - maxHeightPx.floatValue + if (isSpaExpressiveEnabled) { + LaunchedEffect(scrollBehavior?.state?.heightOffsetLimit) { scrollBehavior?.collapse() } + } // Obtain the container Color from the TopAppBarColors using the `collapsedFraction`, as the // bottom part of this TwoRowsTopAppBar changes color at the same rate the app bar expands or diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt index 7d8ee79b3344..60b1e639a2b7 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt @@ -39,7 +39,6 @@ import androidx.compose.ui.tooling.preview.Preview import com.android.settingslib.spa.framework.compose.horizontalValues import com.android.settingslib.spa.framework.compose.verticalValues import com.android.settingslib.spa.framework.theme.SettingsTheme -import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.theme.settingsBackground import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel @@ -56,9 +55,6 @@ fun SettingsScaffold( ) { ActivityTitle(title) val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() - if (isSpaExpressiveEnabled) { - LaunchedEffect(scrollBehavior.state.heightOffsetLimit) { scrollBehavior.collapse() } - } Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), diff --git a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt index 9b1ccef9dadf..62573fe18357 100644 --- a/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt +++ b/packages/SettingsLib/ZeroStatePreference/src/com/android/settingslib/widget/ZeroStatePreference.kt @@ -32,7 +32,7 @@ class ZeroStatePreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { private val iconTint: Int = context.getColor( com.android.settingslib.widget.theme.R.color.settingslib_materialColorOnSecondaryContainer diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 79c3ff9ce989..5a8763f4db6e 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -149,3 +149,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "audio_sharing_developer_option" + namespace: "cross_device_experiences" + description: "Gates whether to enable audio sharing developer option" + bug: "368401233" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 0a8c6bfd7bdf..6ee3bd16148e 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-oudio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoonsok"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Af"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Diensverskaffernetwerk verander tans"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 56420e296287..40c428895c68 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ኦዲዮ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"የማይክሮፎን መሰኪያ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ማይክሮፎን"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ብሉቱዝ ማይክሮፎን"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"አብራ"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"አጥፋ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"የአገልግሎት አቅራቢ አውታረ መረብን በመቀየር ላይ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 88fadbc6cb0a..b54b52198c3a 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"مكبر صوت USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مقبس الميكروفون"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ميكروفون USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ميكروفون يعمل بالبلوتوث"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"مفعّلة"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"إيقاف"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"جارٍ تغيير شبكة مشغِّل شبكة الجوّال."</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index fca2953a788c..a72c5f61d28e 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ইউএছবি অডিঅ\'"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকৰ জেক"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ইউএছবি মাইক্ৰ’ফ’ন"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ব্লুটুথ মাইক্ৰ’ফ’ন"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"অন"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"অফ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"বাহক নেটৱৰ্কৰ পৰিৱৰ্তন"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 5ba618628df9..4cb9ef8ce0ed 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon yuvası"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofonu"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Deaktiv"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator şəbəkəsinin dəyişilməsi"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 28eaeba986c8..43281924b93c 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utikač za mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključeno"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promena mreže mobilnog operatera"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index e1da65e8d5c9..40be0a0f0392 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аўдыяпрылада USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Раздым для мікрафона"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Мікрафон USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Мікрафон з Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Уключана"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Выключана"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Змяненне аператара сеткі"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index b0c235483782..f43758d5b5f5 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Аудиоустройство с USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Жак за микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Микрофон с USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон с Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Включване"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Изключване"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Промяна на мрежата на оператора"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index e861b579a04b..acc8a2db415c 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB অডিও"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"মাইকের জ্যাক"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB মাইক্রোফোন"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT মাইক্রোফোন"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"চালু আছে"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"বন্ধ আছে"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"পরিষেবা প্রদানকারীর নেটওয়ার্ক পরিবর্তন করা হচ্ছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 8844503d05a6..99c0c9ecb02a 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Priključak za mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključi"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključi"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promjena mreže mobilnog operatera"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index b905ae2a1e81..c9a14117b44e 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Àudio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Connector per al micròfon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micròfon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micròfon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activa"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivat"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"S\'està canviant la xarxa de l\'operador de telefonia mòbil"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index a69374f0a91c..ee5fd5ce8059 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofonu"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnout"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Vypnout"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Probíhá změna sítě operátora"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index f14c04b92992..a7308f1c09c0 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lydenhed"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Stik til mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Til"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Fra"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Skift af mobilnetværk"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 86a4cb9addc3..2d0f37b41efb 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Ήχος USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Υποδοχή μικροφώνου"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Μικρόφωνο USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Μικρόφωνο BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ενεργό"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ανενεργό"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Αλλαγή δικτύου εταιρείας κινητής τηλεφωνίας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 7296c9696c8c..96c844d95438 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 7296c9696c8c..96c844d95438 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 7296c9696c8c..96c844d95438 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mic jack"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB microphone"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT microphone"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"On"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Off"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator network changing"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index c933c7e77066..ec88743d32d9 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector para micrófono"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activar"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivar"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de proveedor de red"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 81926e5a5c03..77a0799ca1ac 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-heli"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoni pistikupesa"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Sees"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Väljas"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operaatori võrku muudetakse"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 45c2daa38f5c..a490b7b6a515 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB bidezko audioa"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonoaren konektorea"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB bidezko mikrofonoa"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth bidezko mikrofonoa"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktibatu"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desaktibatu"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operadorearen sarea aldatzen"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index af969248aec1..305fcbeeef43 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"بلندگوی USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"فیش میکروفون"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"میکروفون USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"میکروفون بلوتوث"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"روشن"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"خاموش"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"تغییر شبکه شرکت مخابراتی"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index ab4ac1ea44c0..fc6d5da1250c 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofoniliitäntä"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofoni"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofoni"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Päällä"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ei käytössä"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operaattorin verkko muuttuu"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 2fccfba8ff04..25d4eb8ca8ec 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Conector do micrófono"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrófono USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrófono Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activada"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desactivada"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Cambio de rede do operador"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 3209b07a6faf..dad0ce543d88 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ઑડિયો"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"માઇક જૅક"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB માઇક્રોફોન"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT માઇક્રોફોન"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ચાલુ"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"બંધ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"કૅરીઅર નેટવર્કમાં ફેરફાર થઈ રહ્યો છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index ef93e78e3090..fcfe9a34815e 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"यूएसबी ऑडियो"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक्रोफ़ोन जैक"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"यूएसबी माइक्रोफ़ोन"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ब्लूटूथ माइक्रोफ़ोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"चालू है"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद है"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"मोबाइल और इंटरनेट सेवा देने वाली कंपनी का नेटवर्क बदल रहा है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 7fda17b0e91c..5db8507a6c72 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB zvučnik"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Utičnica za mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Uključeno"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Isključeno"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Promjena mreže mobilnog operatera"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 838965fdeccf..bd700108c92e 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hangeszköz"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jack csatlakozója"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Be"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Ki"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Szolgáltatói hálózat váltása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 6ad700750228..6549de0d1464 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB աուդիո"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Խոսափողի հարակցիչ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB խոսափող"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth խոսափող"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Միացնել"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Անջատել"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Օպերատորի ցանցի փոփոխություն"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index c6f0800ead9b..2555e9b3b918 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Colokan mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktif"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Nonaktif"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Jaringan operator berubah"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 2688e0ba92e8..eba432f30088 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-hljóð"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Hljóðnematengi"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-hljóðnemi"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-hljóðnemi"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Kveikt"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Slökkt"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Skiptir um farsímakerfi"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index f1020caa0db0..b6f863e16b1d 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -416,7 +416,7 @@ <string name="verbose_vendor_logging_notification_action" msgid="1190831050259046071">"הפעלה ליום אחד נוסף"</string> <string name="verbose_vendor_logging_preference_summary_will_disable" msgid="6175431593394522553">"מתבצעת השבתה אחרי יום אחד"</string> <string name="verbose_vendor_logging_preference_summary_on" msgid="9017757242481762036">"בוצעה הפעלה ללא הגבלת זמן"</string> - <string name="window_animation_scale_title" msgid="5236381298376812508">"קנה מידה לאנימציה של חלון"</string> + <string name="window_animation_scale_title" msgid="5236381298376812508">"מהירות אנימציית מעבר במסכים"</string> <string name="transition_animation_scale_title" msgid="1278477690695439337">"קנה מידה לאנימציית מעבר"</string> <string name="animator_duration_scale_title" msgid="7082913931326085176">"קנה מידה למשך זמן אנימציה"</string> <string name="overlay_display_devices_title" msgid="5411894622334469607">"יצירת הדמיה של תצוגות משניות"</string> @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"אודיו ב-USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"שקע למיקרופון"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"מיקרופון ב-USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"מיקרופון BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"פועלת"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"מצב כבוי"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"רשת ספק משתנה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index ae68a03b7f40..ff3870ac5343 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB オーディオ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"マイク差込口"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB マイク"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT マイク"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ON"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"OFF"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"携帯通信会社のネットワークを変更します"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 850ddf19bb42..0ec064832255 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB აუდიო"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"მიკროფონის ჯეკი"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB მიკროფონი"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT მიკროფონი"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ჩართვა"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"გამორთვა"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"ოპერატორის ქსელის შეცვლა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 0cf0e04e97b6..f7547170b846 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофон ұяшығы"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофоны"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Қосу"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Өшіру"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Оператор желісін өзгерту"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 0d22701f69ce..cac7c55685c4 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ឧបករណ៍បំពងសំឡេង USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ឌុយមីក្រូហ្វូន"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"មីក្រូហ្វូន USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"មីក្រូហ្វូន BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"បើក"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"បិទ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"បណ្តាញក្រុមហ៊ុនសេវាទូរសព្ទកំពុងផ្លាស់ប្តូរ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 3dc663a78366..92534ae56b6f 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ಆಡಿಯೋ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ಮೈಕ್ ಜ್ಯಾಕ್"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ಮೈಕ್ರೊಫೋನ್"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ಮೈಕ್ರೊಫೋನ್"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ಆನ್ ಆಗಿದೆ"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ಆಫ್"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"ವಾಹಕ ನೆಟ್ವರ್ಕ್ ಬದಲಾಯಿಸುವಿಕೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 9f714e18d8b2..1bfe19ce2e2e 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 오디오"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"마이크 잭"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 마이크"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"블루투스 마이크"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"사용"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"사용 안 함"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"이동통신사 네트워크 변경"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index d7a39131edf0..1884b0326ed2 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофондун оюкчасы"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофону"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Күйгүзүү"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Өчүрүү"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Байланыш оператору өзгөртүлүүдө"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index e57dae8d4ea4..0f83134aeefa 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"ສຽງ USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ຊ່ອງສຽງໄມ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ໄມໂຄຣໂຟນ USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ໄມໂຄຣໂຟນ BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ເປີດ"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ປິດ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"ການປ່ຽນເຄືອຂ່າຍຜູ້ໃຫ້ບໍລິການ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index a4d5b9bb1581..3a81c917eaa6 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB garsas"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofono jungtis"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofonas"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"„Bluetooth“ mikrofonas"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Įjungta"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Išjungta"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Keičiamas operatoriaus tinklas"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index bf6f80cab0db..8e1f20e32ded 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofona ligzda"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofons"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofons"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ieslēgts"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Izslēgts"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobilo sakaru operatora tīkla mainīšana"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index f664f725b0ab..66de28295292 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудио"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Приклучок за микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Микрофон со Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вклучено"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Исклучено"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Променување на мрежата на операторот"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 49af83d9851e..fe2a996c151f 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ഓഡിയോ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"മൈക്ക് ജാക്ക്"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB മൈക്രോഫോൺ"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT മൈക്രോഫോൺ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ഓണാണ്"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ഓഫാണ്"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"കാരിയർ നെറ്റ്വർക്ക് മാറ്റൽ"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 2d010feba7ad..c9668f169f0d 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофоны чихэвчний оролт"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Асаах"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Унтраах"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Оператор компанийн сүлжээг өөрчилж байна"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index bea1500a9874..600779d1a915 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ऑडिओ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइक जॅक"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB मायक्रोफोन"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT मायक्रोफोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"सुरू करा"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"बंद करा"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"वाहक नेटवर्क बदलत आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index f04549027e0a..d4eb1baabeab 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Bicu mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Hidup"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Mati"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rangkaian pembawa berubah"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 4bdb50f50a78..92f9cbbb4c2b 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB အသံ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"မိုက်ဂျက်ပင်"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB မိုက်ခရိုဖုန်း"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT မိုက်ခရိုဖုန်း"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ဖွင့်"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ပိတ်"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"ဝန်ဆောင်မှုပေးသူ ကွန်ရက် ပြောင်းလဲနေသည်။"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 5de70698c370..83f40afbc9b3 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-lyd"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonkontakt"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Av"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Bytting av operatørnettverk"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index ec5da4572bed..d9d12db7369f 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB अडियो"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"माइकको ज्याक"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB माइक्रोफोन"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT माइक्रोफोन"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"अन छ"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"अफ छ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"सेवा प्रदायकको नेटवर्क परिवर्तन गर्ने आइकन"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index f6c5c9ca764a..f5b54093e659 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Microfoonaansluiting"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-microfoon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-microfoon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aan"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Uit"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Netwerk van provider wordt gewijzigd"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 2ab4d133f2bf..508d2fcd3564 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ଅଡିଓ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ମାଇକ ଜେକ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ମାଇକ୍ରୋଫୋନ"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ମାଇକ୍ରୋଫୋନ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ଚାଲୁ ଅଛି"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ବନ୍ଦ ଅଛି"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"କେରିଅର୍ ନେଟ୍ୱର୍କ ବଦଳୁଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index f054ed538cee..ebff08d9ec79 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ਆਡੀਓ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ਮਾਈਕ ਜੈਕ"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ਚਾਲੂ"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ਬੰਦ"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"ਕੈਰੀਅਰ ਨੈੱਟਵਰਕ ਦੀ ਬਦਲੀ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 3786e02a109e..e5cd6f0bbdb6 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Dźwięk przez USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Gniazdo mikrofonu"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Włączono"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Wyłączono"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Zmiana sieci operatora"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 3a8b2b8ff1bc..7bc0db9fef8d 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desativado"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Alteração de rede da operadora"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 7dbabb8032c3..94a21c8bc36d 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ligado"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desligado"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rede do operador em mudança."</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 3a8b2b8ff1bc..7bc0db9fef8d 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Áudio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Entrada para microfone"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfone USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfone Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Ativado"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Desativado"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Alteração de rede da operadora"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 9e0eb4fd7fde..47ae7efe2652 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Audio USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mufă pentru microfon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Microfon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Microfon BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Activat"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Dezactivat"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Se schimbă rețeaua operatorului"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index dfde27267308..1342321d6dbf 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудиоустройство"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Микрофонный разъем"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-микрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Вкл."</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Выкл."</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Сменить сеть"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index fe011c773c2f..be0356ad2cb9 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ශ්රව්ය"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"මයික් ජැක්කුව"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB මයික්රෆෝනය"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT මයික්රෆෝනය"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ක්රියාත්මකයි"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ක්රියාවිරහිතයි"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"වාහක ජාලය වෙනස් වෙමින්"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 46ab80aefb63..e3a8a8cc2507 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvuk cez USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Konektor mikrofónu"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofón s rozhraním USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofón Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Zapnúť"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Vypnúť"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mení sa sieť operátora"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 0ea0d43eb4cd..717e7baf3634 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Zvok USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Vtič za mikrofon"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofon USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofon Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vklop"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Izklop"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Spreminjanje omrežja operaterja"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 54e75b2d6d61..97e6c7d62a59 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Pajisja audio me USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Fisha e mikrofonit"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Mikrofoni me USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Mikrofoni me Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktive"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Joaktive"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Rrjeti i operatorit celular po ndryshohet"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 742e6ca29160..3cf73a75c703 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB аудио"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Утикач за микрофон"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB микрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth микрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Укључено"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Искључено"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Промена мреже мобилног оператера"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index b7269215e6e1..9586a0c86b67 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-ljud"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofonuttag"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT-mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"På"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Av"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Byter leverantörsnätverk"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index adc21db45db8..8f0b246b1a8c 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Sauti ya USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Pini ya maikrofoni"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Maikrofoni ya USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Maikrofoni ya Bluetooth"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Umewashwa"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Umezimwa"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mabadiliko katika mtandao wa mtoa huduma"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 0e2698582b98..e1a697cdf1bd 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ஆடியோ"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"மைக் ஜாக்"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB மைக்ரோஃபோன்"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT மைக்ரோஃபோன்"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ஆன்"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ஆஃப்"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"மொபைல் நிறுவன நெட்வொர்க்கை மாற்றும்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 0f835fa56d37..22e539ba7da0 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ఆడియో"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"మైక్ జాక్"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB మైక్రోఫోన్"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT మైక్రోఫోన్"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ఆన్లో ఉంది"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ఆఫ్లో ఉంది"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"క్యారియర్ నెట్వర్క్ మారుతోంది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index b5deb55094a1..c9d9309a3ba6 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"เสียง USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"ช่องเสียบไมค์"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"ไมโครโฟน USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"ไมโครโฟน BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"เปิด"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ปิด"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"การเปลี่ยนเครือข่ายผู้ให้บริการ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index b664108ace23..5d7ed8720a00 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Jack ng mikropono"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB na mikropono"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT na mikropono"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Naka-on"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Naka-off"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Nagpapalit ng carrier network"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 7cee87ecc6cb..9e4b6f2d22f8 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB ses cihazı"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon jakı"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT mikrofonu"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Açık"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Kapalı"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operatör ağı değiştiriliyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index dd7f157f47b5..75c6b54dde1d 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB-аудіо"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Гніздо для мікрофона"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB-мікрофон"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth-мікрофон"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Увімкнено"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Вимкнено"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Змінення мережі оператора"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 23162cf5a929..ebd2845d4799 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB آڈیو"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"مائیک جیک"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB مائیکروفون"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"BT مائیکروفون"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"آن"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"آف"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"کیریئر نیٹ ورک کی تبدیلی"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 8fa256fdedb0..60fdf932f95f 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB audio"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Mikrofon ulagichi"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB mikrofon"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Bluetooth mikrofon"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Yoniq"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Oʻchiq"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Mobil tarmoqni o‘zgartirish"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index b4e09e0c36e3..dbd270fb2e57 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Âm thanh qua cổng USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Giắc cắm micrô"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Micrô USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Micrô BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Đang bật"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Đang tắt"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Thay đổi mạng của nhà mạng"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index f9ce1c3df969..5c50e7faf398 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音频"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麦克风插孔"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麦克风"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"蓝牙麦克风"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"开启"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"关闭"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"运营商网络正在更改"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index c50c7ba90165..877fa7b10d9b 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"關閉"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"流動網絡供應商網絡正在變更"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index c5885dea3ae8..fa016ba028e7 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"USB 音訊"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"麥克風插孔"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"USB 麥克風"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"藍牙麥克風"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"開啟"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"關閉"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"電信業者網路正在進行變更"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index a39b436ff652..063b6e7937ea 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -690,8 +690,7 @@ <string name="media_transfer_usb_audio_name" msgid="1789292056757821355">"Umsindo we-USB"</string> <string name="media_transfer_wired_device_mic_name" msgid="7417067197803840965">"Umgodi we-earphone ye-mic"</string> <string name="media_transfer_usb_device_mic_name" msgid="7171789543226269822">"Imakrofoni ye-USB"</string> - <!-- no translation found for media_transfer_bt_device_mic_name (1870669402238687618) --> - <skip /> + <string name="media_transfer_bt_device_mic_name" msgid="1870669402238687618">"Imakrofoni ye-BT"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Vuliwe"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Valiwe"</string> <string name="carrier_network_change_mode" msgid="4257621815706644026">"Inethiwekhi yenkampani yenethiwekhi iyashintsha"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 739c7d6bcfca..abc58ee99904 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1640,13 +1640,13 @@ <string name="media_transfer_wired_headphone_name">Wired headphone</string> <!-- Name of the 3.5mm headphone, used in desktop devices. [CHAR LIMIT=50] --> - <string name="media_transfer_headphone_name">Headphone</string> + <string name="media_transfer_headphone_name">Wired audio</string> <!-- Name of the usb audio device speaker, used in desktop devices. [CHAR LIMIT=50] --> <string name="media_transfer_usb_audio_name">USB audio</string> <!-- Name of the 3.5mm audio device mic. [CHAR LIMIT=50] --> - <string name="media_transfer_wired_device_mic_name">Mic jack</string> + <string name="media_transfer_wired_device_mic_name">Wired microphone</string> <!-- Name of the usb audio device mic. [CHAR LIMIT=50] --> <string name="media_transfer_usb_device_mic_name">USB microphone</string> diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 1a99d25786ff..65b22758946d 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -39,6 +39,7 @@ android_library { "configinfra_framework_flags_java_lib", "device_config_service_flags_java", "libaconfig_java_proto_lite", + "notification_flags_lib", "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayUtils", ], diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 6c3183191163..ebeee8564d2f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -29,6 +29,7 @@ import android.hardware.display.ColorDisplayManager; import android.icu.util.ULocale; import android.media.AudioManager; import android.media.RingtoneManager; +import android.media.Utils; import android.net.Uri; import android.os.LocaleList; import android.os.RemoteException; @@ -309,6 +310,13 @@ public class SettingsHelper { return SILENT_RINGTONE; } } else { + // If the ringtone/notification support the vibration, use the original value. + final int ringtoneType = getRingtoneType(name); + if ((ringtoneType == RingtoneManager.TYPE_RINGTONE + || ringtoneType == RingtoneManager.TYPE_NOTIFICATION) + && hasVibrationSettings(value, ringtoneType)) { + return value; + } return getCanonicalRingtoneValue(value); } } @@ -362,6 +370,15 @@ public class SettingsHelper { return; } + // If the ringtone/notification has vibration, we backup original value in onBackupValue. + // So use the value directly for restoring. + if ((ringtoneType == RingtoneManager.TYPE_RINGTONE + || ringtoneType == RingtoneManager.TYPE_NOTIFICATION) + && hasVibrationSettings(value, ringtoneType)) { + RingtoneManager.setActualDefaultRingtoneUri(mContext, ringtoneType, Uri.parse(value)); + return; + } + Uri ringtoneUri = null; try { ringtoneUri = @@ -617,6 +634,19 @@ public class SettingsHelper { return allLocales.remove(toFullLocale(filteredLocale)); } + private boolean hasVibrationSettings(String value, int type) { + if (Utils.hasVibration(Uri.parse(value)) && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported)) { + if (type == RingtoneManager.TYPE_RINGTONE) { + return android.media.audio.Flags.enableRingtoneHapticsCustomization(); + } + if (type == RingtoneManager.TYPE_NOTIFICATION) { + return com.android.server.notification.Flags.notificationVibrationInSoundUri(); + } + } + return false; + } + /** * Sets the locale specified. Input data is the byte representation of comma separated * multiple BCP-47 language tags. For backwards compatibility, strings of the form diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java index 4b10b56f49fb..cea2bbc5c535 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java @@ -37,9 +37,12 @@ import android.content.res.Resources; import android.database.Cursor; import android.database.MatrixCursor; import android.media.AudioManager; +import android.media.Utils; import android.net.Uri; import android.os.Bundle; import android.os.LocaleList; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.BaseColumns; import android.provider.MediaStore; import android.provider.Settings; @@ -54,8 +57,11 @@ import com.android.internal.R; import org.junit.After; import org.junit.Before; +import org.junit.FixMethodOrder; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -63,6 +69,7 @@ import org.mockito.MockitoAnnotations; * Tests for the SettingsHelperTest */ @RunWith(AndroidJUnit4.class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) public class SettingsHelperTest { private static final String SETTING_KEY = "setting_key"; private static final String SETTING_VALUE = "setting_value"; @@ -74,9 +81,13 @@ public class SettingsHelperTest { "content://media/internal/audio/media/20?title=DefaultNotification&canonical=1"; private static final String DEFAULT_ALARM_VALUE = "content://media/internal/audio/media/30?title=DefaultAlarm&canonical=1"; + private static final String VIBRATION_FILE_NAME = "haptics.xml"; private SettingsHelper mSettingsHelper; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock private Context mContext; @Mock private Resources mResources; @Mock private AudioManager mAudioManager; @@ -120,6 +131,22 @@ public class SettingsHelperTest { } @Test + @EnableFlags({android.media.audio.Flags.FLAG_ENABLE_RINGTONE_HAPTICS_CUSTOMIZATION, + com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI}) + public void testOnBackupValue_ringtoneVibrationSupport_returnsSameValue() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported)).thenReturn( + true); + String testRingtoneVibrationValue = createUriWithVibration(DEFAULT_RINGTONE_VALUE); + String testNotificationVibrationValue = createUriWithVibration(DEFAULT_NOTIFICATION_VALUE); + + assertEquals(testRingtoneVibrationValue, mSettingsHelper.onBackupValue( + Settings.System.RINGTONE, testRingtoneVibrationValue)); + assertEquals(testNotificationVibrationValue, mSettingsHelper.onBackupValue( + Settings.System.NOTIFICATION_SOUND, testNotificationVibrationValue)); + } + + @Test public void testGetRealValue_settingNotReplaced_returnsSameValue() { when(mSettingsHelper.isReplacedSystemSetting(eq(SETTING_KEY))).thenReturn(false); @@ -675,6 +702,30 @@ public class SettingsHelperTest { .isEqualTo(null); } + @Test + @EnableFlags({android.media.audio.Flags.FLAG_ENABLE_RINGTONE_HAPTICS_CUSTOMIZATION, + com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI}) + public void testRestoreValue_ringtoneVibrationSupport_restoreValue() { + when(mResources.getBoolean( + com.android.internal.R.bool.config_ringtoneVibrationSettingsSupported)).thenReturn( + true); + String testRingtoneVibrationValue = createUriWithVibration(DEFAULT_RINGTONE_VALUE); + String testNotificationVibrationValue = createUriWithVibration(DEFAULT_NOTIFICATION_VALUE); + ContentProvider mockMediaContentProvider = + new MockContentProvider(mContext) { + @Override + public String getType(Uri url) { + return "audio/ogg"; + } + }; + mContentResolver.addProvider(MediaStore.AUTHORITY, mockMediaContentProvider); + resetRingtoneSettingsToDefault(); + + assertRingtoneSettingsRestoring(Settings.System.RINGTONE, testRingtoneVibrationValue); + assertRingtoneSettingsRestoring( + Settings.System.NOTIFICATION_SOUND, testNotificationVibrationValue); + } + private static class MockSettingsProvider extends MockContentProvider { private final ArrayMap<String, String> mKeyValueStore = new ArrayMap<>(); MockSettingsProvider(Context context) { @@ -766,4 +817,25 @@ public class SettingsHelperTest { assertThat(Settings.System.getString(mContentResolver, Settings.System.ALARM_ALERT)) .isEqualTo(DEFAULT_ALARM_VALUE); } + + private String createUriWithVibration(String defaultUriString) { + return Uri.parse(defaultUriString).buildUpon() + .appendQueryParameter( + Utils.VIBRATION_URI_PARAM, VIBRATION_FILE_NAME).build().toString(); + } + + private void assertRingtoneSettingsRestoring( + String settings, String testRingtoneSettingsValue) { + mSettingsHelper.restoreValue( + mContext, + mContentResolver, + new ContentValues(), + Uri.EMPTY, + settings, + testRingtoneSettingsValue, + 0); + + assertThat(Settings.System.getString(mContentResolver, settings)) + .isEqualTo(testRingtoneSettingsValue); + } } diff --git a/packages/StatementService/Parser/Android.bp b/packages/StatementService/Parser/Android.bp new file mode 100644 index 000000000000..c8af1344f4c7 --- /dev/null +++ b/packages/StatementService/Parser/Android.bp @@ -0,0 +1,26 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package { + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "StatementServiceParser", + use_resource_processor: true, + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + target_sdk_version: "29", +} diff --git a/packages/StatementService/Parser/AndroidManifest.xml b/packages/StatementService/Parser/AndroidManifest.xml new file mode 100644 index 000000000000..a3a99ac0552d --- /dev/null +++ b/packages/StatementService/Parser/AndroidManifest.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statementservice.parser"> +</manifest>
\ No newline at end of file diff --git a/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt new file mode 100644 index 000000000000..4314f86fad37 --- /dev/null +++ b/packages/StatementService/Parser/src/com/android/statementservice/parser/DalComponentParser.kt @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("DalComponentParser") + +package com.android.statementservice.parser + +import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB +import android.os.PatternMatcher.PATTERN_LITERAL +import android.os.PatternMatcher.PATTERN_PREFIX +import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB + +/** + * Parses a DAL component matching expression to Android's {@link android.os.PatternMatcher} type + * and pattern. Matching expressions support the following wildcards: + * + * 1) An asterisk (*) matches zero to as many characters as possible + * 2) A question mark (?) matches any single character. + * + * Matching one to many characters can be done with a question mark followed by an asterisk (?+). + * + * @param expression A matching expression string from a DAL relation extension component used for + * matching a URI part. This must be a non-empty string and all characters in the + * string should be decoded. + * + * @return Returns a Pair containing a {@link android.os.PatternMatcher} type and pattern. + */ +fun parseMatchingExpression(expression: String): Pair<Int, String> { + if (expression.isNullOrEmpty()) { + throw IllegalArgumentException("Matching expressions cannot be an empty string") + } + var count = 0 + var isAdvanced = expression.contains("?*") + val pattern = buildString { + for (char in expression) { + when (char) { + '*' -> { + if (this.endsWith('.') && !this.endsWith("\\.")) { + append('+') + } else { + count += 1 + append(".*") + } + } + '?' -> { + count += 1 + append('.') + } + '.' -> { + append("\\.") + } + '[', ']', '{', '}' -> { + if (isAdvanced) { + append('\\') + } + append(char) + } + else -> append(char) + } + } + } + if (count == 0) { + return Pair(PATTERN_LITERAL, pattern) + } + if (count == 1 && pattern.endsWith(".*")) { + return Pair(PATTERN_PREFIX, pattern.dropLast(2)) + } + if (isAdvanced) { + return Pair(PATTERN_ADVANCED_GLOB, pattern) + } + return Pair(PATTERN_SIMPLE_GLOB, pattern) +}
\ No newline at end of file diff --git a/packages/StatementService/TEST_MAPPING b/packages/StatementService/TEST_MAPPING new file mode 100644 index 000000000000..0714c9366665 --- /dev/null +++ b/packages/StatementService/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit" : [ + { + "name": "StatementServiceTests" + } + ] +}
\ No newline at end of file diff --git a/packages/StatementService/tests/Android.bp b/packages/StatementService/tests/Android.bp new file mode 100644 index 000000000000..ec1bd96a0bc0 --- /dev/null +++ b/packages/StatementService/tests/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: ["frameworks_base_license"], + default_team: "trendy_team_framework_android_packages", +} + +android_test { + name: "StatementServiceTests", + use_resource_processor: true, + test_suites: ["general-tests"], + srcs: ["src/**/*.kt"], + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.runner", + "StatementServiceParser", + "truth", + ], +} diff --git a/packages/StatementService/tests/AndroidManifest.xml b/packages/StatementService/tests/AndroidManifest.xml new file mode 100644 index 000000000000..bde0f953252c --- /dev/null +++ b/packages/StatementService/tests/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.statementservice.test"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.statementservice.test" /> +</manifest> diff --git a/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.kt new file mode 100644 index 000000000000..44a56ec91458 --- /dev/null +++ b/packages/StatementService/tests/src/com/android/statementservice/parser/DalComponentParserTest.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.statementservice.parser + +import android.os.PatternMatcher.PATTERN_ADVANCED_GLOB +import android.os.PatternMatcher.PATTERN_LITERAL +import android.os.PatternMatcher.PATTERN_PREFIX +import android.os.PatternMatcher.PATTERN_SIMPLE_GLOB +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class DalComponentParserTest { + + @Test + fun parseExpressions() { + validateParsedExpression("foobar", PATTERN_LITERAL, "foobar") + validateParsedExpression("foo.bar", PATTERN_LITERAL, "foo\\.bar") + validateParsedExpression("foo*", PATTERN_PREFIX, "foo") + validateParsedExpression("*bar", PATTERN_SIMPLE_GLOB, ".*bar") + validateParsedExpression("foo*bar", PATTERN_SIMPLE_GLOB, "foo.*bar") + validateParsedExpression("foo.*bar", PATTERN_SIMPLE_GLOB, "foo\\..*bar") + validateParsedExpression("*foo*bar", PATTERN_SIMPLE_GLOB, ".*foo.*bar") + validateParsedExpression("foo?bar", PATTERN_SIMPLE_GLOB, "foo.bar") + validateParsedExpression("foo.?bar", PATTERN_SIMPLE_GLOB, "foo\\..bar") + validateParsedExpression("?bar", PATTERN_SIMPLE_GLOB, ".bar") + validateParsedExpression("foo?", PATTERN_SIMPLE_GLOB, "foo.") + validateParsedExpression("fo?b*r", PATTERN_SIMPLE_GLOB, "fo.b.*r") + validateParsedExpression("?*bar", PATTERN_ADVANCED_GLOB, ".+bar") + validateParsedExpression("foo?*bar", PATTERN_ADVANCED_GLOB, "foo.+bar") + validateParsedExpression("foo?*bar*", PATTERN_ADVANCED_GLOB, "foo.+bar.*") + validateParsedExpression("foo*?bar", PATTERN_SIMPLE_GLOB, "foo.*.bar") + + // set matches are not supported in DAL + validateParsedExpression("foo[a-z]", PATTERN_LITERAL, "foo[a-z]") + validateParsedExpression("foo[a-z]+", PATTERN_LITERAL, "foo[a-z]+") + validateParsedExpression("foo[a-z]*", PATTERN_PREFIX, "foo[a-z]") + validateParsedExpression("[a-z]*bar", PATTERN_SIMPLE_GLOB, "[a-z].*bar") + validateParsedExpression("foo[a-z]?bar", PATTERN_SIMPLE_GLOB, "foo[a-z].bar") + validateParsedExpression("foo[a-z]?*bar", PATTERN_ADVANCED_GLOB, "foo\\[a-z\\].+bar") + + // range matches are not supported in DAL + validateParsedExpression("fo{2}", PATTERN_LITERAL, "fo{2}") + validateParsedExpression("fo{2}+", PATTERN_LITERAL, "fo{2}+") + validateParsedExpression("fo{2}*", PATTERN_PREFIX, "fo{2}") + validateParsedExpression("fo{2}*bar", PATTERN_SIMPLE_GLOB, "fo{2}.*bar") + validateParsedExpression("fo{2}?*", PATTERN_ADVANCED_GLOB, "fo\\{2\\}.+") + validateParsedExpression("foo{2}?*bar", PATTERN_ADVANCED_GLOB, "foo\\{2\\}.+bar") + } + + @Test(expected = IllegalArgumentException::class) + fun parseEmptyExpression() { + parseMatchingExpression("") + } + + private fun validateParsedExpression(given: String, expectedType: Int, expectedFilter: String) { + val (type, filter) = parseMatchingExpression(given) + assertThat(filter).isEqualTo(expectedFilter) + assertThat(type).isEqualTo(expectedType) + assertThat(filter).isEqualTo(expectedFilter) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 28635318236d..d7b968338b89 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -899,9 +899,9 @@ android_robolectric_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", "truth", ], @@ -936,9 +936,9 @@ android_robolectric_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", "truth", ], @@ -974,9 +974,9 @@ android_ravenwood_test { "androidx.compose.runtime_runtime", ], libs: [ - "android.test.runner.stubs.system", - "android.test.base.stubs.system", - "android.test.mock.stubs.system", + "android.test.runner.impl", + "android.test.base.impl", + "android.test.mock.impl", ], auto_gen_config: true, plugins: [ diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a21a80506279..c79c0441cfbb 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -395,9 +395,9 @@ flag { } flag { - name: "status_bar_ron_chips" + name: "status_bar_notification_chips" namespace: "systemui" - description: "Show rich ongoing notifications as chips in the status bar" + description: "Show promoted ongoing notifications as chips in the status bar" bug: "361346412" } @@ -1481,3 +1481,9 @@ flag { bug: "370863642" } +flag { + name: "notes_role_qs_tile" + namespace: "systemui" + description: "Enables notes role qs tile which opens default notes role app in app bubbles" + bug: "357863750" +} 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 93a99bd948f1..18f40c98fe04 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -136,20 +136,16 @@ constructor( ) /** - * The timings when animating a View into an app using a spring animator. - * - * Important: since springs don't have fixed durations, these timings represent fractions of - * the progress between the spring's initial value and its final value. - * - * TODO(b/372858592): make this a separate class explicitly using percentages. + * The timings when animating a View into an app using a spring animator. These timings + * represent fractions of the progress between the spring's initial value and its final + * value. */ val SPRING_TIMINGS = - TransitionAnimator.Timings( - totalDuration = 1000L, - contentBeforeFadeOutDelay = 0L, - contentBeforeFadeOutDuration = 800L, - contentAfterFadeInDelay = 850L, - contentAfterFadeInDuration = 135L, + TransitionAnimator.SpringTimings( + contentBeforeFadeOutDelay = 0f, + contentBeforeFadeOutDuration = 0.8f, + contentAfterFadeInDelay = 0.85f, + contentAfterFadeInDuration = 0.135f, ) /** 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 1d8ff77ac719..9dc93484a638 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt @@ -52,7 +52,7 @@ class TransitionAnimator( private val interpolators: Interpolators, /** [springTimings] and [springInterpolators] must either both be null or both not null. */ - private val springTimings: Timings? = null, + private val springTimings: SpringTimings? = null, private val springInterpolators: Interpolators? = null, private val springParams: SpringParams = DEFAULT_SPRING_PARAMS, ) { @@ -83,8 +83,22 @@ class TransitionAnimator( delay: Long, duration: Long, ): Float { + return getProgressInternal( + timings.totalDuration.toFloat(), + linearProgress, + delay.toFloat(), + duration.toFloat(), + ) + } + + private fun getProgressInternal( + totalDuration: Float, + linearProgress: Float, + delay: Float, + duration: Float, + ): Float { return MathUtils.constrain( - (linearProgress * timings.totalDuration - delay) / duration, + (linearProgress * totalDuration - delay) / duration, 0.0f, 1.0f, ) @@ -367,6 +381,25 @@ class TransitionAnimator( val contentAfterFadeInDuration: Long, ) + /** + * The timings (durations and delays) used by the multi-spring animator. These are expressed as + * fractions of 1, similar to how the progress of an animator can be expressed as a float value + * between 0 and 1. + */ + class SpringTimings( + /** The portion of animation to wait before fading out the expanding content. */ + val contentBeforeFadeOutDelay: Float, + + /** The portion of animation during which the expanding content fades out. */ + val contentBeforeFadeOutDuration: Float, + + /** The portion of animation to wait before fading in the expanded content. */ + val contentAfterFadeInDelay: Float, + + /** The portion of animation during which the expanded content fades in. */ + val contentAfterFadeInDuration: Float, + ) + /** The interpolators used by this animator. */ data class Interpolators( /** The interpolator used for the Y position, width, height and corner radius. */ @@ -576,18 +609,14 @@ class TransitionAnimator( } override fun onAnimationEnd(animation: Animator) { - if (DEBUG) { - Log.d(TAG, "Animation ended") - } - - // TODO(b/330672236): Post this to the main thread instead so that it does not - // flicker with Flexiglass enabled. - controller.onTransitionAnimationEnd(isExpandingFullyAbove) - transitionContainerOverlay.remove(windowBackgroundLayer) - - if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) { - openingWindowSyncViewOverlay?.remove(windowBackgroundLayer) - } + onAnimationEnd( + controller, + isExpandingFullyAbove, + windowBackgroundLayer, + transitionContainerOverlay, + openingWindowSyncViewOverlay, + moveBackgroundLayerWhenAppVisibilityChanges, + ) } } ) @@ -1021,34 +1050,47 @@ class TransitionAnimator( cornerRadii[7] = state.bottomCornerRadius drawable.cornerRadii = cornerRadii - val timings: Timings val interpolators: Interpolators + val fadeInProgress: Float + val fadeOutProgress: Float if (useSpring) { - timings = springTimings!! interpolators = springInterpolators!! + val timings = springTimings!! + fadeInProgress = + getProgressInternal( + totalDuration = 1f, + linearProgress, + timings.contentBeforeFadeOutDelay, + timings.contentBeforeFadeOutDuration, + ) + fadeOutProgress = + getProgressInternal( + totalDuration = 1f, + linearProgress, + timings.contentAfterFadeInDelay, + timings.contentAfterFadeInDuration, + ) } else { - timings = this.timings interpolators = this.interpolators + fadeInProgress = + getProgress( + timings, + linearProgress, + timings.contentBeforeFadeOutDelay, + timings.contentBeforeFadeOutDuration, + ) + fadeOutProgress = + getProgress( + timings, + linearProgress, + timings.contentAfterFadeInDelay, + timings.contentAfterFadeInDuration, + ) } // We first fade in the background layer to hide the expanding view, then fade it out with // SRC mode to draw a hole punch in the status bar and reveal the opening window (if // needed). If !isLaunching, the reverse happens. - val fadeInProgress = - getProgress( - timings, - linearProgress, - timings.contentBeforeFadeOutDelay, - timings.contentBeforeFadeOutDuration, - ) - val fadeOutProgress = - getProgress( - timings, - linearProgress, - timings.contentAfterFadeInDelay, - timings.contentAfterFadeInDuration, - ) - if (isLaunching) { if (fadeInProgress < 1) { val alpha = diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt index 6b3223df9532..125664110d6a 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/AndroidColorScheme.kt @@ -18,6 +18,7 @@ package com.android.compose.theme import android.content.Context import androidx.annotation.ColorRes +import androidx.compose.runtime.Immutable import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color import com.android.internal.R @@ -38,23 +39,41 @@ val LocalAndroidColorScheme = * [androidx.compose.material3.MaterialTheme]. For other colors (e.g. primary), use * `MaterialTheme.colorScheme` instead. */ -class AndroidColorScheme(val context: Context) { - val primaryFixed = color(context, R.color.system_primary_fixed) - val primaryFixedDim = color(context, R.color.system_primary_fixed_dim) - val onPrimaryFixed = color(context, R.color.system_on_primary_fixed) - val onPrimaryFixedVariant = color(context, R.color.system_on_primary_fixed_variant) - val secondaryFixed = color(context, R.color.system_secondary_fixed) - val secondaryFixedDim = color(context, R.color.system_secondary_fixed_dim) - val onSecondaryFixed = color(context, R.color.system_on_secondary_fixed) - val onSecondaryFixedVariant = color(context, R.color.system_on_secondary_fixed_variant) - val tertiaryFixed = color(context, R.color.system_tertiary_fixed) - val tertiaryFixedDim = color(context, R.color.system_tertiary_fixed_dim) - val onTertiaryFixed = color(context, R.color.system_on_tertiary_fixed) - val onTertiaryFixedVariant = color(context, R.color.system_on_tertiary_fixed_variant) - +@Immutable +class AndroidColorScheme( + val primaryFixed: Color, + val primaryFixedDim: Color, + val onPrimaryFixed: Color, + val onPrimaryFixedVariant: Color, + val secondaryFixed: Color, + val secondaryFixedDim: Color, + val onSecondaryFixed: Color, + val onSecondaryFixedVariant: Color, + val tertiaryFixed: Color, + val tertiaryFixedDim: Color, + val onTertiaryFixed: Color, + val onTertiaryFixedVariant: Color, +) { companion object { internal fun color(context: Context, @ColorRes id: Int): Color { return Color(context.resources.getColor(id, context.theme)) } + + operator fun invoke(context: Context): AndroidColorScheme { + return AndroidColorScheme( + primaryFixed = color(context, R.color.system_primary_fixed), + primaryFixedDim = color(context, R.color.system_primary_fixed_dim), + onPrimaryFixed = color(context, R.color.system_on_primary_fixed), + onPrimaryFixedVariant = color(context, R.color.system_on_primary_fixed_variant), + secondaryFixed = color(context, R.color.system_secondary_fixed), + secondaryFixedDim = color(context, R.color.system_secondary_fixed_dim), + onSecondaryFixed = color(context, R.color.system_on_secondary_fixed), + onSecondaryFixedVariant = color(context, R.color.system_on_secondary_fixed_variant), + tertiaryFixed = color(context, R.color.system_tertiary_fixed), + tertiaryFixedDim = color(context, R.color.system_tertiary_fixed_dim), + onTertiaryFixed = color(context, R.color.system_on_tertiary_fixed), + onTertiaryFixedVariant = color(context, R.color.system_on_tertiary_fixed_variant), + ) + } } } 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 96c47cc5fb6f..4ab526188ffe 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 @@ -765,7 +765,7 @@ private fun BoxScope.CommunalHubLazyGrid( alpha = { outlineAlpha }, modifier = Modifier.requiredSize(dpSize).thenIf( - dragDropState.draggingItemIndex != index + dragDropState.draggingItemKey != item.key ) { Modifier.animateItem( placementSpec = spring(stiffness = Spring.StiffnessMediumLow) @@ -778,7 +778,7 @@ private fun BoxScope.CommunalHubLazyGrid( dragDropState = dragDropState, selected = selected, enabled = item.isWidgetContent(), - index = index, + key = item.key, ) { isDragging -> CommunalContent( modifier = Modifier.fillMaxSize(), 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 101385f88825..0718bc331d41 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 @@ -38,8 +38,9 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.round import androidx.compose.ui.unit.toOffset import androidx.compose.ui.unit.toSize import com.android.systemui.Flags.communalWidgetResizing @@ -93,7 +94,7 @@ internal constructor( private val scope: CoroutineScope, private val updateDragPositionForRemove: (offset: Offset) -> Boolean, ) { - var draggingItemIndex by mutableStateOf<Int?>(null) + var draggingItemKey by mutableStateOf<Any?>(null) private set var isDraggingToRemove by mutableStateOf(false) @@ -105,6 +106,8 @@ internal constructor( private var draggingItemInitialOffset by mutableStateOf(Offset.Zero) private var dragStartPointerOffset by mutableStateOf(Offset.Zero) + private var previousTargetItemKey: Any? = null + internal val draggingItemOffset: Offset get() = draggingItemLayoutInfo?.let { item -> @@ -112,7 +115,7 @@ internal constructor( } ?: Offset.Zero private val draggingItemLayoutInfo: LazyGridItemInfo? - get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.index == draggingItemIndex } + get() = state.layoutInfo.visibleItemsInfo.firstOrNull { it.key == draggingItemKey } /** * Called when dragging is initiated. @@ -137,7 +140,7 @@ internal constructor( .firstItemAtOffset(normalizedOffset - contentOffset) ?.apply { dragStartPointerOffset = normalizedOffset - this.offset.toOffset() - draggingItemIndex = index + draggingItemKey = key draggingItemInitialOffset = this.offset.toOffset() return true } @@ -146,16 +149,19 @@ internal constructor( } internal fun onDragInterrupted() { - draggingItemIndex?.let { + draggingItemKey?.let { if (isDraggingToRemove) { - contentListState.onRemove(it) + contentListState.onRemove( + contentListState.list.indexOfFirst { it.key == draggingItemKey } + ) isDraggingToRemove = false updateDragPositionForRemove(Offset.Zero) } // persist list editing changes on dragging ends contentListState.onSaveList() - draggingItemIndex = null + draggingItemKey = null } + previousTargetItemKey = null draggingItemDraggedDelta = Offset.Zero draggingItemInitialOffset = Offset.Zero dragStartPointerOffset = Offset.Zero @@ -170,15 +176,29 @@ internal constructor( val startOffset = draggingItem.offset.toOffset() + draggingItemOffset val endOffset = startOffset + draggingItem.size.toSize() val middleOffset = startOffset + (endOffset - startOffset) / 2f + val draggingBoundingBox = + IntRect(draggingItem.offset + draggingItemOffset.round(), draggingItem.size) val targetItem = - state.layoutInfo.visibleItemsInfo - .asSequence() - .filter { item -> contentListState.isItemEditable(item.index) } - .filter { item -> draggingItem.index != item.index } - .firstItemAtOffset(middleOffset) + if (communalWidgetResizing()) { + state.layoutInfo.visibleItemsInfo.findLast { item -> + val itemBoundingBox = IntRect(item.offset, item.size) + draggingItemKey != item.key && + contentListState.isItemEditable(item.index) && + draggingBoundingBox.contains(itemBoundingBox.center) + } + } else { + state.layoutInfo.visibleItemsInfo + .asSequence() + .filter { item -> contentListState.isItemEditable(item.index) } + .filter { item -> draggingItem.index != item.index } + .firstItemAtOffset(middleOffset) + } - if (targetItem != null) { + if ( + targetItem != null && + (!communalWidgetResizing() || targetItem.key != previousTargetItemKey) + ) { val scrollToIndex = if (targetItem.index == state.firstVisibleItemIndex) { draggingItem.index @@ -187,6 +207,14 @@ internal constructor( } else { null } + if (communalWidgetResizing()) { + // Keep track of the previous target item, to avoid rapidly oscillating between + // items if the target item doesn't visually move as a result of the index change. + // In this case, even after the index changes, we'd still be colliding with the + // element, so it would be selected as the target item the next time this function + // runs again, which would trigger us to revert the index change we recently made. + previousTargetItemKey = targetItem.key + } if (scrollToIndex != null) { scope.launch { // this is needed to neutralize automatic keeping the first item first. @@ -196,20 +224,17 @@ internal constructor( } else { contentListState.onMove(draggingItem.index, targetItem.index) } - draggingItemIndex = targetItem.index isDraggingToRemove = false - } else { + } else if (targetItem == null) { val overscroll = checkForOverscroll(startOffset, endOffset) if (overscroll != 0f) { scrollChannel.trySend(overscroll) } isDraggingToRemove = checkForRemove(startOffset) + previousTargetItemKey = null } } - private val LazyGridItemInfo.offsetEnd: IntOffset - get() = this.offset + this.size - /** Calculate the amount dragged out of bound on both sides. Returns 0f if not overscrolled */ private fun checkForOverscroll(startOffset: Offset, endOffset: Offset): Float { return when { @@ -237,7 +262,7 @@ fun Modifier.dragContainer( viewModel: BaseCommunalViewModel, ): Modifier { return this.then( - pointerInput(dragDropState, contentOffset) { + Modifier.pointerInput(dragDropState, contentOffset) { detectDragGesturesAfterLongPress( onDrag = { change, offset -> change.consume() @@ -273,7 +298,7 @@ fun Modifier.dragContainer( @Composable fun LazyGridItemScope.DraggableItem( dragDropState: GridDragDropState, - index: Int, + key: Any, enabled: Boolean, selected: Boolean, modifier: Modifier = Modifier, @@ -283,7 +308,7 @@ fun LazyGridItemScope.DraggableItem( return content(false) } - val dragging = index == dragDropState.draggingItemIndex + val dragging = key == dragDropState.draggingItemKey val itemAlpha: Float by animateFloatAsState( targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt index a525f36c71ce..9390664d1283 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt @@ -30,14 +30,10 @@ import androidx.compose.ui.unit.IntRect import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope -import com.android.keyguard.LockIconView -import com.android.keyguard.LockIconViewController import com.android.systemui.biometrics.AuthController import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines import com.android.systemui.keyguard.ui.view.DeviceEntryIconView @@ -61,7 +57,6 @@ constructor( private val windowManager: WindowManager, private val authController: AuthController, private val featureFlags: FeatureFlagsClassic, - private val lockIconViewController: Lazy<LockIconViewController>, private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>, private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>, private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>, @@ -71,42 +66,28 @@ constructor( ) { @Composable fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) { - if (!KeyguardBottomAreaRefactor.isEnabled && !DeviceEntryUdfpsRefactor.isEnabled) { - return - } - val context = LocalContext.current AndroidView( factory = { context -> - val view = - if (DeviceEntryUdfpsRefactor.isEnabled) { - DeviceEntryIconView( - context, - null, - logger = LongPressHandlingViewLogger(logBuffer, tag = TAG) - ) - .apply { - id = R.id.device_entry_icon_view - DeviceEntryIconViewBinder.bind( - applicationScope, - this, - deviceEntryIconViewModel.get(), - deviceEntryForegroundViewModel.get(), - deviceEntryBackgroundViewModel.get(), - falsingManager.get(), - vibratorHelper.get(), - overrideColor, - ) - } - } else { - // KeyguardBottomAreaRefactor.isEnabled - LockIconView(context, null).apply { - id = R.id.lock_icon_view - lockIconViewController.get().setLockIconView(this) - } + DeviceEntryIconView( + context, + null, + logger = LongPressHandlingViewLogger(logBuffer, tag = TAG), + ) + .apply { + id = R.id.device_entry_icon_view + DeviceEntryIconViewBinder.bind( + applicationScope, + this, + deviceEntryIconViewModel.get(), + deviceEntryForegroundViewModel.get(), + deviceEntryBackgroundViewModel.get(), + falsingManager.get(), + vibratorHelper.get(), + overrideColor, + ) } - view }, modifier = modifier.element(LockIconElementKey).layout { measurable, _ -> @@ -141,9 +122,7 @@ constructor( * On devices that support UDFPS (under-display fingerprint sensor), the bounds of the icon are * the same as the bounds of the sensor. */ - private fun lockIconBounds( - context: Context, - ): IntRect { + private fun lockIconBounds(context: Context): IntRect { val windowViewBounds = windowManager.currentWindowMetrics.bounds var widthPx = windowViewBounds.right.toFloat() if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { @@ -162,10 +141,7 @@ constructor( val (center, radius) = if (authController.isUdfpsSupported && udfpsLocation != null) { Pair( - IntOffset( - x = udfpsLocation.x, - y = udfpsLocation.y, - ), + IntOffset(x = udfpsLocation.x, y = udfpsLocation.y), authController.udfpsRadius.toInt(), ) } else { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt index 2e39524baaad..73c4fab7b646 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/WeatherClockSection.kt @@ -33,7 +33,7 @@ import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding -import com.android.systemui.customization.R as customizationR +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockElementKeys import com.android.systemui.keyguard.ui.composable.modifier.burnInAware import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel @@ -57,12 +57,12 @@ constructor( Row( modifier = Modifier.padding( - horizontal = dimensionResource(customizationR.dimen.clock_padding_start) + horizontal = dimensionResource(customR.dimen.clock_padding_start) ) .burnInAware(aodBurnInViewModel, burnInParams, isClock = true) ) { WeatherElement( - weatherClockElementViewId = customizationR.id.weather_clock_time, + weatherClockElementViewId = customR.id.weather_clock_time, clock = clock, elementKey = WeatherClockElementKeys.timeElementKey, ) @@ -75,7 +75,7 @@ constructor( modifier: Modifier = Modifier, ) { WeatherElement( - weatherClockElementViewId = customizationR.id.weather_clock_date, + weatherClockElementViewId = customR.id.weather_clock_date, clock = clock, elementKey = WeatherClockElementKeys.dateElementKey, modifier = modifier, @@ -88,7 +88,7 @@ constructor( modifier: Modifier = Modifier, ) { WeatherElement( - weatherClockElementViewId = customizationR.id.weather_clock_weather_icon, + weatherClockElementViewId = customR.id.weather_clock_weather_icon, clock = clock, elementKey = WeatherClockElementKeys.weatherIconElementKey, modifier = modifier.wrapContentSize(), @@ -101,7 +101,7 @@ constructor( modifier: Modifier = Modifier, ) { WeatherElement( - weatherClockElementViewId = customizationR.id.weather_clock_alarm_dnd, + weatherClockElementViewId = customR.id.weather_clock_alarm_dnd, clock = clock, elementKey = WeatherClockElementKeys.dndAlarmElementKey, modifier = modifier.wrapContentSize(), @@ -114,7 +114,7 @@ constructor( modifier: Modifier = Modifier, ) { WeatherElement( - weatherClockElementViewId = customizationR.id.weather_clock_temperature, + weatherClockElementViewId = customR.id.weather_clock_temperature, clock = clock, elementKey = WeatherClockElementKeys.temperatureElementKey, modifier = modifier.wrapContentSize(), @@ -159,7 +159,7 @@ constructor( modifier = Modifier.height(IntrinsicSize.Max) .padding( - horizontal = dimensionResource(customizationR.dimen.clock_padding_start) + horizontal = dimensionResource(customR.dimen.clock_padding_start) ) .burnInAware(aodBurnInViewModel, burnInParams, isClock = true) ) { @@ -168,7 +168,7 @@ constructor( modifier = Modifier.fillMaxSize() .padding( - start = dimensionResource(customizationR.dimen.clock_padding_start) + start = dimensionResource(customR.dimen.clock_padding_start) ) ) { Weather(clock = clock, modifier = Modifier.align(Alignment.TopStart)) diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt index 8b9e9274b448..e4c60e166fd5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt @@ -18,6 +18,8 @@ package com.android.systemui.notifications.ui.composable import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.util.fastCoerceAtLeast +import androidx.compose.ui.util.fastCoerceAtMost import com.android.compose.nestedscroll.PriorityNestedScrollConnection /** @@ -44,7 +46,7 @@ fun NotificationScrimNestedScrollConnection( orientation = Orientation.Vertical, // scrolling up and inner content is taller than the scrim, so scrim needs to // expand; content can scroll once scrim is at the minScrimOffset. - canStartPreScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0 && offsetBeforeStart == 0f && contentHeight() > minVisibleScrimHeight() && @@ -52,36 +54,38 @@ fun NotificationScrimNestedScrollConnection( }, // scrolling down and content is done scrolling to top. After that, the scrim // needs to collapse; collapse the scrim until it is at the maxScrimOffset. - canStartPostScroll = { offsetAvailable, _ -> + canStartPostScroll = { offsetAvailable, _, _ -> offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll()) }, canStartPostFling = { false }, - canContinueScroll = { - val currentHeight = scrimOffset() - minScrimOffset() < currentHeight && currentHeight < maxScrimOffset - }, - canScrollOnFling = true, + canStopOnPreFling = { false }, onStart = { offsetAvailable -> onStart(offsetAvailable) }, - onScroll = { offsetAvailable -> + onScroll = { offsetAvailable, _ -> val currentHeight = scrimOffset() val amountConsumed = if (offsetAvailable > 0) { val amountLeft = maxScrimOffset - currentHeight - offsetAvailable.coerceAtMost(amountLeft) + offsetAvailable.fastCoerceAtMost(amountLeft) } else { val amountLeft = minScrimOffset() - currentHeight - offsetAvailable.coerceAtLeast(amountLeft) + offsetAvailable.fastCoerceAtLeast(amountLeft) } snapScrimOffset(currentHeight + amountConsumed) amountConsumed }, - // Don't consume the velocity on pre/post fling onStop = { velocityAvailable -> onStop(velocityAvailable) if (scrimOffset() < minScrimOffset()) { animateScrimOffset(minScrimOffset()) } - { 0f } + // Don't consume the velocity on pre/post fling + 0f + }, + onCancel = { + onStop(0f) + if (scrimOffset() < minScrimOffset()) { + animateScrimOffset(minScrimOffset()) + } }, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt index a706585deebc..edb05ebd77d1 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastCoerceAtLeast import com.android.compose.nestedscroll.PriorityNestedScrollConnection import kotlin.math.max import kotlin.math.roundToInt @@ -86,21 +87,25 @@ fun NotificationStackNestedScrollConnection( ): PriorityNestedScrollConnection { return PriorityNestedScrollConnection( orientation = Orientation.Vertical, - canStartPreScroll = { _, _ -> false }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { _, _, _ -> false }, + canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward() }, canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() }, - canContinueScroll = { stackOffset() > 0f }, - canScrollOnFling = true, + canStopOnPreFling = { false }, onStart = { offsetAvailable -> onStart(offsetAvailable) }, - onScroll = { offsetAvailable -> - onScroll(offsetAvailable) - offsetAvailable + onScroll = { offsetAvailable, _ -> + val minOffset = 0f + val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset()) + if (consumed != 0f) { + onScroll(consumed) + } + consumed }, onStop = { velocityAvailable -> onStop(velocityAvailable) - suspend { velocityAvailable } + velocityAvailable }, + onCancel = { onStop(0f) }, ) } 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 54497f621c73..2a91bd8b1d73 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 @@ -133,7 +133,14 @@ fun SceneScope.QuickSettingsLayout( Column( verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding), horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding), + modifier = + modifier + .fillMaxWidth() + .padding( + start = QuickSettingsShade.Dimensions.Padding, + end = QuickSettingsShade.Dimensions.Padding, + top = QuickSettingsShade.Dimensions.Padding, + ), ) { BrightnessSliderContainer( viewModel = viewModel.brightnessSliderViewModel, 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 085157ac72b9..9f99c372e2a8 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,9 +27,10 @@ import com.android.compose.animation.scene.content.Content import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.nestedscroll.PriorityNestedScrollConnection -import com.android.compose.nestedscroll.SuspendedValue import kotlin.math.absoluteValue +internal typealias SuspendedValue<T> = suspend () -> T + internal interface DraggableHandler { /** * Start a drag in the given [startedPosition], with the given [overSlop] and number of @@ -612,7 +613,7 @@ internal class NestedScrollHandlerImpl( return PriorityNestedScrollConnection( orientation = orientation, - canStartPreScroll = { offsetAvailable, offsetBeforeStart -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> canChangeScene = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f @@ -630,7 +631,13 @@ internal class NestedScrollHandlerImpl( return@PriorityNestedScrollConnection false } - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo // If the current swipe transition is *not* closed to 0f or 1f, then we want the // scroll events to intercept the current transition to continue the scene @@ -638,7 +645,7 @@ internal class NestedScrollHandlerImpl( isIntercepting = true true }, - canStartPostScroll = { offsetAvailable, offsetBeforeStart -> + canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ -> val behavior: NestedScrollBehavior = when { offsetAvailable > 0f -> topOrLeftBehavior @@ -649,7 +656,12 @@ internal class NestedScrollHandlerImpl( val isZeroOffset = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo val canStart = when (behavior) { @@ -684,7 +696,12 @@ internal class NestedScrollHandlerImpl( // We could start an overscroll animation canChangeScene = false - _lastPointersInfo = pointersInfoOwner.pointersInfo() + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + _lastPointersInfo = pointersInfo val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable) if (canStart) { @@ -693,8 +710,7 @@ internal class NestedScrollHandlerImpl( canStart }, - canContinueScroll = { true }, - canScrollOnFling = false, + canStopOnPreFling = { true }, onStart = { offsetAvailable -> val pointersInfo = pointersInfo() dragController = @@ -704,19 +720,33 @@ internal class NestedScrollHandlerImpl( overSlop = if (isIntercepting) 0f else offsetAvailable, ) }, - onScroll = { offsetAvailable -> + onScroll = { offsetAvailable, _ -> val controller = dragController ?: error("Should be called after onStart") + val pointersInfo = pointersInfoOwner.pointersInfo() + if (pointersInfo.isMouseWheel) { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection 0f + } + // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is // initiated in a nested child. controller.onDrag(delta = offsetAvailable) }, onStop = { velocityAvailable -> val controller = dragController ?: error("Should be called after onStart") - - controller - .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) - .also { dragController = null } + try { + controller + .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene) + .invoke() + } finally { + dragController = null + } + }, + onCancel = { + val controller = dragController ?: error("Should be called after onStart") + controller.onStop(velocity = 0f, canChangeContent = canChangeScene) + dragController = null }, ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index aa70a0ce156b..8613f6da0f62 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource 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.PointerEventType import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope @@ -184,6 +185,7 @@ internal class MultiPointerDraggableNode( private var startedPosition: Offset? = null private var pointersDown: Int = 0 + private var isMouseWheel: Boolean = false internal fun pointersInfo(): PointersInfo { return PointersInfo( @@ -191,6 +193,7 @@ internal class MultiPointerDraggableNode( startedPosition = startedPosition, // We could have 0 pointers during fling or for other reasons. pointersDown = pointersDown.coerceAtLeast(1), + isMouseWheel = isMouseWheel, ) } @@ -202,8 +205,15 @@ internal class MultiPointerDraggableNode( // [requireAncestorPointersInfoOwner], to our descendants. while (currentContext.isActive) { // During the Initial pass, we receive the event after our ancestors. - val changes = awaitPointerEvent(PointerEventPass.Initial).changes + val pointerEvent = awaitPointerEvent(PointerEventPass.Initial) + + // Ignore cursor has entered the input region. + // This will only be sent after the cursor is hovering when in the input region. + if (pointerEvent.type == PointerEventType.Enter) continue + + val changes = pointerEvent.changes pointersDown = changes.countDown() + isMouseWheel = pointerEvent.type == PointerEventType.Scroll when { // There are no more pointers down. @@ -223,7 +233,8 @@ internal class MultiPointerDraggableNode( // The first pointer down, startedPosition was not set. startedPosition == null -> { - val firstPointerDown = changes.single() + // Mouse wheel could start with multiple pointer down + val firstPointerDown = changes.first() velocityPointerId = firstPointerDown.id velocityTracker.resetTracking() velocityTracker.addPointerInputChange(firstPointerDown) @@ -647,4 +658,8 @@ internal fun interface PointersInfoOwner { fun pointersInfo(): PointersInfo } -internal data class PointersInfo(val startedPosition: Offset?, val pointersDown: Int) +internal data class PointersInfo( + val startedPosition: Offset?, + val pointersDown: Int, + val isMouseWheel: Boolean, +) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 2657d7cf8156..3c3c612c028b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -430,6 +430,10 @@ internal class MutableSceneTransitionLayoutStateImpl( // Replace the transition. transitionStates = transitionStates.subList(0, transitionStates.lastIndex) + transition + + // Make sure it is removed from the finishedTransitions set if it was already + // finished. + finishedTransitions.remove(currentState) } else { // Append the new transition. transitionStates = transitionStates + transition 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 205267da151a..f0043e1e89b0 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 @@ -27,7 +27,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified -import com.android.compose.nestedscroll.SuspendedValue import kotlin.math.absoluteValue import kotlinx.coroutines.CompletableDeferred 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 4ae323517b26..ecf64b771d1f 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 @@ -18,6 +18,8 @@ package com.android.compose.nestedscroll import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.util.fastCoerceAtLeast +import androidx.compose.ui.util.fastCoerceAtMost /** * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the @@ -43,35 +45,32 @@ fun LargeTopAppBarNestedScrollConnection( 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 -> + canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> offsetAvailable < 0 && offsetBeforeStart == 0f && 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]. - canStartPostScroll = { offsetAvailable, _ -> + canStartPostScroll = { offsetAvailable, _, _ -> offsetAvailable > 0 && height() < maxHeight() }, canStartPostFling = { false }, - canContinueScroll = { - val currentHeight = height() - minHeight() < currentHeight && currentHeight < maxHeight() - }, - canScrollOnFling = true, + canStopOnPreFling = { false }, onStart = { /* do nothing */ }, - onScroll = { offsetAvailable -> + onScroll = { offsetAvailable, _ -> val currentHeight = height() val amountConsumed = if (offsetAvailable > 0) { val amountLeft = maxHeight() - currentHeight - offsetAvailable.coerceAtMost(amountLeft) + offsetAvailable.fastCoerceAtMost(amountLeft) } else { val amountLeft = minHeight() - currentHeight - offsetAvailable.coerceAtLeast(amountLeft) + offsetAvailable.fastCoerceAtLeast(amountLeft) } onHeightChanged(currentHeight + amountConsumed) amountConsumed }, // Don't consume the velocity on pre/post fling - onStop = { { 0f } }, + onStop = { 0f }, + onCancel = { /* do nothing */ }, ) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt index a3641e6635e7..636c55799ff2 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt @@ -16,37 +16,59 @@ package com.android.compose.nestedscroll +import androidx.compose.animation.core.AnimationState +import androidx.compose.animation.core.DecayAnimationSpec +import androidx.compose.animation.core.animateDecay import androidx.compose.foundation.gestures.Orientation import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.unit.Velocity import com.android.compose.ui.util.SpaceVectorConverter +import kotlin.math.abs import kotlin.math.sign - -internal typealias SuspendedValue<T> = suspend () -> T +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope /** - * This [NestedScrollConnection] waits for a child to scroll ([onPreScroll] or [onPostScroll]), and - * then decides (via [canStartPreScroll] or [canStartPostScroll]) if it should take over scrolling. - * If it does, it will scroll before its children, until [canContinueScroll] allows it. + * A [NestedScrollConnection] that intercepts scroll events in priority mode. * - * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop] - * after [onStart]. + * Priority mode allows this connection to take control over scroll events within a nested scroll + * hierarchy. When in priority mode, this connection consumes scroll events before its children, + * enabling custom scrolling behaviors like sticky headers. * + * @param orientation The orientation of the scroll. + * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll + * events in pre-scroll mode. + * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll + * events in post-scroll mode. + * @param canStartPostFling lambda that returns true if the connection can start consuming scroll + * events in post-fling mode. + * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll + * events in pre-fling (i.e. as soon as the user lifts their fingers). + * @param onStart lambda that is called when the connection starts consuming scroll events. + * @param onScroll lambda that is called when the connection consumes a scroll event and returns the + * consumed amount. + * @param onStop lambda that is called when the connection stops consuming scroll events and returns + * the consumed velocity. + * @param onCancel lambda that is called when the connection is cancelled. * @sample LargeTopAppBarNestedScrollConnection * @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection */ class PriorityNestedScrollConnection( orientation: Orientation, - private val canStartPreScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, - private val canStartPostScroll: (offsetAvailable: Float, offsetBeforeStart: Float) -> Boolean, + private val canStartPreScroll: + (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, + private val canStartPostScroll: + (offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean, private val canStartPostFling: (velocityAvailable: Float) -> Boolean, - private val canContinueScroll: (source: NestedScrollSource) -> Boolean, - private val canScrollOnFling: Boolean, + private val canStopOnPreFling: () -> Boolean, private val onStart: (offsetAvailable: Float) -> Unit, - private val onScroll: (offsetAvailable: Float) -> Float, - private val onStop: (velocityAvailable: Float) -> SuspendedValue<Float>, + private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float, + private val onStop: suspend (velocityAvailable: Float) -> Float, + private val onCancel: () -> Unit, ) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) { /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */ @@ -54,6 +76,9 @@ class PriorityNestedScrollConnection( private var offsetScrolledBeforePriorityMode = 0f + /** This job allows us to interrupt the onStop animation */ + private var onStopJob: Deferred<Float> = CompletableDeferred(0f) + override fun onPostScroll( consumed: Offset, available: Offset, @@ -64,62 +89,48 @@ class PriorityNestedScrollConnection( // the beginning or from the last fling gesture. val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat - if ( - isPriorityMode || - (source == NestedScrollSource.SideEffect && !canScrollOnFling) || - !canStartPostScroll(availableFloat, offsetBeforeStart) - ) { + if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) { // The priority mode cannot start so we won't consume the available offset. return Offset.Zero } - return onPriorityStart(availableFloat).toOffset() + return start(availableFloat, source).toOffset() } override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { if (!isPriorityMode) { - if (source == NestedScrollSource.UserInput || canScrollOnFling) { - val availableFloat = available.toFloat() - if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode)) { - return onPriorityStart(availableFloat).toOffset() - } - // We want to track the amount of offset consumed before entering priority mode - offsetScrolledBeforePriorityMode += availableFloat + val availableFloat = available.toFloat() + if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) { + return start(availableFloat, source).toOffset() } - - return Offset.Zero - } - - val availableFloat = available.toFloat() - if (!canContinueScroll(source)) { - // Step 3a: We have lost priority and we no longer need to intercept scroll events. - onPriorityStop(velocity = 0f) - - // We've just reset offsetScrolledBeforePriorityMode to 0f // We want to track the amount of offset consumed before entering priority mode offsetScrolledBeforePriorityMode += availableFloat - return Offset.Zero } - // Step 2: We have the priority and can consume the scroll events. - return onScroll(availableFloat).toOffset() + return scroll(available.toFloat(), source).toOffset() } override suspend fun onPreFling(available: Velocity): Velocity { - if (isPriorityMode && canScrollOnFling) { - // We don't want to consume the velocity, we prefer to continue receiving scroll events. + if (!isPriorityMode) { + resetOffsetTracker() return Velocity.Zero } - // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed - // of the fling gesture. - return onPriorityStop(velocity = available.toFloat()).invoke().toVelocity() + + if (canStopOnPreFling()) { + // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the + // velocity of the fling gesture. + return stop(velocityAvailable = available.toFloat()).toVelocity() + } + + // We don't want to consume the velocity, we prefer to continue receiving scroll events. + return Velocity.Zero } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { val availableFloat = available.toFloat() if (isPriorityMode) { - return onPriorityStop(velocity = availableFloat).invoke().toVelocity() + return stop(velocityAvailable = availableFloat).toVelocity() } if (!canStartPostFling(availableFloat)) { @@ -131,10 +142,14 @@ class PriorityNestedScrollConnection( // TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the // overscroll behavior on the Scene level. val smallOffset = availableFloat.sign - onPriorityStart(availableOffset = smallOffset) + start( + availableOffset = smallOffset, + source = NestedScrollSource.SideEffect, + skipScroll = true, + ) // This is the last event of a scroll gesture. - return onPriorityStop(availableFloat).invoke().toVelocity() + return stop(availableFloat).toVelocity() } /** @@ -143,36 +158,76 @@ class PriorityNestedScrollConnection( * TODO(b/303224944) This method should be removed. */ fun reset() { - // Step 3c: To ensure that an onStop is always called for every onStart. - onPriorityStop(velocity = 0f) + if (isPriorityMode) { + // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart. + cancel() + } else { + resetOffsetTracker() + } } - private fun onPriorityStart(availableOffset: Float): Float { - if (isPriorityMode) { - error("This should never happen, onPriorityStart() was called when isPriorityMode") + private fun shouldStop(consumed: Float): Boolean { + return consumed == 0f + } + + private fun start( + availableOffset: Float, + source: NestedScrollSource, + skipScroll: Boolean = false, + ): Float { + check(!isPriorityMode) { + "This should never happen, start() was called when isPriorityMode" } // Step 1: It's our turn! We start capturing scroll events when one of our children has an // available offset following a scroll event. isPriorityMode = true + onStopJob.cancel() + // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is // lifted (step 3b), or this object has been destroyed (step 3c). onStart(availableOffset) - return onScroll(availableOffset) + return if (skipScroll) 0f else scroll(availableOffset, source) } - private fun onPriorityStop(velocity: Float): SuspendedValue<Float> { - // We can restart tracking the consumed offsets from scratch. - offsetScrolledBeforePriorityMode = 0f + private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float { + // Step 2: We have the priority and can consume the scroll events. + val consumedByScroll = onScroll(offsetAvailable, source) - if (!isPriorityMode) { - return { 0f } + if (shouldStop(consumedByScroll)) { + // Step 3a: We have lost priority and we no longer need to intercept scroll events. + cancel() + + // We've just reset offsetScrolledBeforePriorityMode to 0f + // We want to track the amount of offset consumed before entering priority mode + offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll } + return consumedByScroll + } + + /** Reset the tracking of consumed offsets before entering in priority mode. */ + private fun resetOffsetTracker() { + offsetScrolledBeforePriorityMode = 0f + } + + private suspend fun stop(velocityAvailable: Float): Float { + check(isPriorityMode) { "This should never happen, stop() was called before start()" } isPriorityMode = false + resetOffsetTracker() - return onStop(velocity) + return coroutineScope { + onStopJob = async { onStop(velocityAvailable) } + onStopJob.await() + } + } + + private fun cancel() { + check(isPriorityMode) { "This should never happen, cancel() was called before start()" } + isPriorityMode = false + resetOffsetTracker() + onCancel() } } 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 ecef6be49df8..a0fed9064181 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 @@ -39,7 +39,6 @@ import com.android.compose.animation.scene.TestScenes.SceneC import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.Transition import com.android.compose.animation.scene.subjects.assertThat -import com.android.compose.nestedscroll.SuspendedValue import com.android.compose.test.MonotonicClockTestScope import com.android.compose.test.runMonotonicClockTest import com.android.compose.test.transition @@ -128,7 +127,7 @@ class DraggableHandlerTest { val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal) var pointerInfoOwner: () -> PointersInfo = { - PointersInfo(startedPosition = Offset.Zero, pointersDown = 1) + PointersInfo(startedPosition = Offset.Zero, pointersDown = 1, isMouseWheel = false) } fun nestedScrollConnection( @@ -1188,7 +1187,9 @@ class DraggableHandlerTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) // Drag from the **top** of the screen - pointerInfoOwner = { PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1) } + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = false) + } assertIdle(currentScene = SceneC) nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) @@ -1206,7 +1207,11 @@ class DraggableHandlerTest { // Drag from the **bottom** of the screen pointerInfoOwner = { - PointersInfo(startedPosition = Offset(0f, SCREEN_SIZE), pointersDown = 1) + PointersInfo( + startedPosition = Offset(0f, SCREEN_SIZE), + pointersDown = 1, + isMouseWheel = false, + ) } assertIdle(currentScene = SceneC) @@ -1221,6 +1226,22 @@ class DraggableHandlerTest { } @Test + fun ignoreMouseWheel() = runGestureTest { + // Start at scene C. + navigateToSceneC() + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) + + // Use mouse wheel + pointerInfoOwner = { + PointersInfo(startedPosition = Offset(0f, 0f), pointersDown = 1, isMouseWheel = true) + } + assertIdle(currentScene = SceneC) + + nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) + assertIdle(currentScene = SceneC) + } + + @Test fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest { // Swipe up from the middle to transition to scene B. val middle = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f) diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt index c8f6e6d99933..3df608717414 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt @@ -46,7 +46,6 @@ import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Velocity import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.modifiers.thenIf -import com.android.compose.nestedscroll.SuspendedValue import com.google.common.truth.Truth.assertThat import kotlin.properties.Delegates import kotlinx.coroutines.coroutineScope diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index f3a34884c756..f5bb5ba032c2 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -727,4 +727,45 @@ class SceneTransitionLayoutStateTest { // The previous job is cancelled and does not infinitely collect the progress. job.join() } + + @Test + fun replacedTransitionIsRemovedFromFinishedTransitions() = runTest { + val state = MutableSceneTransitionLayoutState(SceneA) + + val aToB = + transition( + SceneA, + SceneB, + onFreezeAndAnimate = { + // Do nothing, so that this transition stays in the transitionStates list and we + // can finish() it manually later. + }, + ) + val replacingAToB = transition(SceneB, SceneC) + val replacingBToC = transition(SceneB, SceneC, replacedTransition = replacingAToB) + + // Start A => B. + val aToBJob = state.startTransitionImmediately(animationScope = this, aToB) + + // Start B => C and immediately finish it. It will be flagged as finished in + // STLState.finishedTransitions given that A => B is not finished yet. + val bToCJob = state.startTransitionImmediately(animationScope = this, replacingAToB) + replacingAToB.finish() + bToCJob.join() + + // Start a new B => C that replaces the previously finished B => C. + val replacingBToCJob = + state.startTransitionImmediately(animationScope = this, replacingBToC) + + // Finish A => B. + aToB.finish() + aToBJob.join() + + // Finish the new B => C. + replacingBToC.finish() + replacingBToCJob.join() + + assertThat(state.transitionState).isIdle() + assertThat(state.transitionState).hasCurrentScene(SceneC) + } } 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 28d0a473935d..3001505d0e02 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 @@ -17,6 +17,8 @@ package com.android.compose.animation.scene 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.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -39,12 +41,14 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.ScrollWheel import androidx.compose.ui.test.assertPositionInRootIsEqualTo import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performMouseInput import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeRight import androidx.compose.ui.test.swipeUp @@ -102,26 +106,22 @@ class SwipeToSceneTest { modifier = Modifier.size(LayoutWidth, LayoutHeight).testTag(TestElements.Foo.debugName), ) { scene( - SceneA, + key = SceneA, userActions = if (swipesEnabled()) - mapOf( - Swipe.Left to SceneB, - Swipe.Down to TestScenes.SceneC, - Swipe.Up to SceneB, - ) + mapOf(Swipe.Left to SceneB, Swipe.Down to SceneC, Swipe.Up to SceneB) else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( - SceneB, + key = SceneB, userActions = if (swipesEnabled()) mapOf(Swipe.Right to SceneA) else emptyMap(), ) { Box(Modifier.fillMaxSize()) } scene( - TestScenes.SceneC, + key = SceneC, userActions = if (swipesEnabled()) mapOf( @@ -196,7 +196,7 @@ class SwipeToSceneTest { // Drag is in progress, so currentScene = SceneA and progress = 56dp / LayoutHeight transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) assertThat(transition).hasCurrentScene(SceneA) assertThat(transition).hasProgress(56.dp / LayoutHeight) assertThat(transition).isInitiatedByUserInput() @@ -206,15 +206,15 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { up() } transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(TestScenes.SceneC) - assertThat(transition).hasCurrentScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) + assertThat(transition).hasCurrentScene(SceneC) assertThat(transition).hasProgress(56.dp / LayoutHeight) assertThat(transition).isInitiatedByUserInput() // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test @@ -271,20 +271,20 @@ class SwipeToSceneTest { // We should be animating to C (currentScene = SceneC). transition = assertThat(layoutState.transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) - assertThat(transition).hasToScene(TestScenes.SceneC) - assertThat(transition).hasCurrentScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) + assertThat(transition).hasCurrentScene(SceneC) assertThat(transition).hasProgress(55.dp / LayoutHeight) // Wait for the animation to finish. We should now be in scene C. rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test fun multiPointerSwipe() { // Start at scene C. - val layoutState = layoutState(TestScenes.SceneC) + val layoutState = layoutState(SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. @@ -295,7 +295,7 @@ class SwipeToSceneTest { } assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) // Swipe down with two fingers. rule.onRoot().performTouchInput { @@ -307,7 +307,7 @@ class SwipeToSceneTest { // We are transitioning to B because we used 2 fingers. val transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasFromScene(TestScenes.SceneC) + assertThat(transition).hasFromScene(SceneC) assertThat(transition).hasToScene(SceneB) // Release the fingers and wait for the animation to end. We are back to C because we only @@ -315,13 +315,13 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { repeat(2) { i -> up(pointerId = i) } } rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test fun defaultEdgeSwipe() { // Start at scene C. - val layoutState = layoutState(TestScenes.SceneC) + val layoutState = layoutState(SceneC) // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is // detected as a drag event. @@ -332,7 +332,7 @@ class SwipeToSceneTest { } assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) // Swipe down from the top edge. rule.onRoot().performTouchInput { @@ -342,7 +342,7 @@ class SwipeToSceneTest { // We are transitioning to B (and not A) because we started from the top edge. var transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasFromScene(TestScenes.SceneC) + assertThat(transition).hasFromScene(SceneC) assertThat(transition).hasToScene(SceneB) // Release the fingers and wait for the animation to end. We are back to C because we only @@ -350,7 +350,7 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { up() } rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) // Swipe right from the left edge. rule.onRoot().performTouchInput { @@ -360,7 +360,7 @@ class SwipeToSceneTest { // We are transitioning to B (and not A) because we started from the left edge. transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasFromScene(TestScenes.SceneC) + assertThat(transition).hasFromScene(SceneC) assertThat(transition).hasToScene(SceneB) // Release the fingers and wait for the animation to end. We are back to C because we only @@ -368,7 +368,7 @@ class SwipeToSceneTest { rule.onRoot().performTouchInput { up() } rule.waitForIdle() assertThat(layoutState.transitionState).isIdle() - assertThat(layoutState.transitionState).hasCurrentScene(TestScenes.SceneC) + assertThat(layoutState.transitionState).hasCurrentScene(SceneC) } @Test @@ -434,7 +434,7 @@ class SwipeToSceneTest { // We should still correctly compute that we are swiping down to scene C. var transition = assertThat(layoutState.transitionState).isSceneTransition() - assertThat(transition).hasToScene(TestScenes.SceneC) + assertThat(transition).hasToScene(SceneC) // Release the finger, animating back to scene A. rule.onRoot().performTouchInput { up() } @@ -502,6 +502,89 @@ class SwipeToSceneTest { } @Test + fun mouseWheel_pointerInputApi_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + TestContent(layoutState) + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + } + + @Test + fun mouseWheel_scrollableCannotScroll_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Box( + Modifier.fillMaxSize() + // A scrollable that does not consume the scroll gesture + .scrollable(rememberScrollableState { 0f }, Orientation.Vertical) + ) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + } + + @Test + fun mouseWheel_scrollableConsume_ignoredByStl() { + val layoutState = layoutState() + var touchSlop = 0f + var lastScroll = 0f + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Box( + Modifier.fillMaxSize() + // A scrollable that consumes the scroll gesture + .scrollable( + rememberScrollableState { + lastScroll = it + it + }, + Orientation.Vertical, + ) + ) + } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + } + } + + rule.onRoot().performMouseInput { + enter(middle) + scroll(touchSlop * 10, ScrollWheel.Vertical) + } + + // Mouse wheel scroll are ignored + assertThat(layoutState.currentTransition).isNull() + + // Mouse wheel scroll can still be consumed by the scrollable + assertThat(lastScroll).isNotEqualTo(0f) + assertThat(touchSlop).isNotEqualTo(0f) + } + + @Test fun transitionKey() { val transitionkey = TransitionKey(debugName = "foo") val state = diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt index badc43bd3e0f..1a3b86b936df 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt @@ -34,30 +34,31 @@ class PriorityNestedScrollConnectionTest { private var canStartPreScroll = false private var canStartPostScroll = false private var canStartPostFling = false - private var canContinueScroll = false + private var canStopOnPreFling = true private var isStarted = false private var lastScroll: Float? = null - private var returnOnScroll = 0f + private var consumeScroll = true private var lastStop: Float? = null - private var returnOnStop = 0f + private var isCancelled: Boolean = false + private var consumeStop = true private val scrollConnection = PriorityNestedScrollConnection( orientation = Orientation.Vertical, - canStartPreScroll = { _, _ -> canStartPreScroll }, - canStartPostScroll = { _, _ -> canStartPostScroll }, + canStartPreScroll = { _, _, _ -> canStartPreScroll }, + canStartPostScroll = { _, _, _ -> canStartPostScroll }, canStartPostFling = { canStartPostFling }, - canContinueScroll = { canContinueScroll }, - canScrollOnFling = false, + canStopOnPreFling = { canStopOnPreFling }, onStart = { isStarted = true }, - onScroll = { - lastScroll = it - returnOnScroll + onScroll = { offsetAvailable, _ -> + lastScroll = offsetAvailable + if (consumeScroll) offsetAvailable else 0f }, onStop = { lastStop = it - { returnOnStop } + if (consumeStop) it else 0f }, + onCancel = { isCancelled = true }, ) @Test @@ -85,7 +86,7 @@ class PriorityNestedScrollConnectionTest { canStartPostScroll = true scrollConnection.onPostScroll( consumed = Offset.Zero, - available = Offset.Zero, + available = Offset(1f, 1f), source = UserInput, ) } @@ -136,45 +137,55 @@ class PriorityNestedScrollConnectionTest { @Test fun step2_onPriorityMode_shouldContinueIfAllowed() { startPriorityModePostScroll() - canContinueScroll = true - scrollConnection.onPreScroll(available = Offset(1f, 1f), source = UserInput) + val scroll1 = scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput) assertThat(lastScroll).isEqualTo(1f) + assertThat(scroll1.y).isEqualTo(1f) - canContinueScroll = false - scrollConnection.onPreScroll(available = Offset(2f, 2f), source = UserInput) - assertThat(lastScroll).isNotEqualTo(2f) - assertThat(lastScroll).isEqualTo(1f) + consumeScroll = false + val scroll2 = scrollConnection.onPreScroll(available = Offset(0f, 2f), source = UserInput) + assertThat(lastScroll).isEqualTo(2f) + assertThat(scroll2.y).isEqualTo(0f) } @Test - fun step3a_onPriorityMode_shouldStopIfCannotContinue() { + fun step3a_onPriorityMode_shouldCancelIfCannotContinue() { startPriorityModePostScroll() - canContinueScroll = false + consumeScroll = false - scrollConnection.onPreScroll(available = Offset.Zero, source = UserInput) + scrollConnection.onPreScroll(available = Offset(0f, 1f), source = UserInput) - assertThat(lastStop).isNotNull() + assertThat(isCancelled).isTrue() } @Test fun step3b_onPriorityMode_shouldStopOnFling() = runTest { startPriorityModePostScroll() - canContinueScroll = true scrollConnection.onPreFling(available = Velocity.Zero) - assertThat(lastStop).isNotNull() + assertThat(lastStop).isEqualTo(0f) + } + + @Test + fun ifCannotStopOnPreFling_shouldStopOnPostFling() = runTest { + startPriorityModePostScroll() + canStopOnPreFling = false + + scrollConnection.onPreFling(available = Velocity.Zero) + assertThat(lastStop).isNull() + + scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero) + assertThat(lastStop).isEqualTo(0f) } @Test - fun step3c_onPriorityMode_shouldStopOnReset() { + fun step3c_onPriorityMode_shouldCancelOnReset() { startPriorityModePostScroll() - canContinueScroll = true scrollConnection.reset() - assertThat(lastStop).isNotNull() + assertThat(isCancelled).isTrue() } @Test diff --git a/packages/SystemUI/customization/res/values/ids.xml b/packages/SystemUI/customization/res/values/ids.xml index ec466f041179..3a3e06bdd377 100644 --- a/packages/SystemUI/customization/res/values/ids.xml +++ b/packages/SystemUI/customization/res/values/ids.xml @@ -1,5 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <resources> + <item type="id" name="lockscreen_clock_view_large" /> + <item type="id" name="lockscreen_clock_view" /> + <!-- View ids for elements in large weather clock --> <item type="id" name="weather_clock_time" /> <item type="id" name="weather_clock_date" /> diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt index 9b94c91a348c..eedddb28ff89 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt @@ -30,16 +30,16 @@ import com.android.systemui.plugins.clocks.ClockFaceEvents import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData -import com.android.systemui.shared.clocks.view.DigitalClockFaceView import com.android.systemui.shared.clocks.view.FlexClockView +import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView import java.util.Locale import java.util.TimeZone class ComposedDigitalLayerController( private val ctx: Context, - private val assets: AssetLoader, + private val resources: Resources, + private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources private val layer: ComposedDigitalHandLayer, - private val isLargeClock: Boolean, messageBuffer: MessageBuffer, ) : SimpleClockLayerController { private val logger = Logger(messageBuffer, ComposedDigitalLayerController::class.simpleName!!) @@ -48,34 +48,22 @@ class ComposedDigitalLayerController( val dozeState = DefaultClockController.AnimationState(1F) var isRegionDark = true - override var view: DigitalClockFaceView = - when (layer.customizedView) { - "FlexClockView" -> FlexClockView(ctx, assets, messageBuffer) - else -> { - throw IllegalStateException("CustomizedView string is not valid") - } - } - - // Matches LayerControllerConstructor - internal constructor( - ctx: Context, - assets: AssetLoader, - layer: ClockLayer, - isLargeClock: Boolean, - messageBuffer: MessageBuffer, - ) : this(ctx, assets, layer as ComposedDigitalHandLayer, isLargeClock, messageBuffer) + override val view = FlexClockView(ctx, assets, messageBuffer) init { layer.digitalLayers.forEach { + val childView = SimpleDigitalClockTextView(ctx, messageBuffer) val controller = - SimpleClockLayerController.Factory.create( + SimpleDigitalHandLayerController( ctx, + resources, assets, - it, - isLargeClock, + it as DigitalHandLayer, + childView, messageBuffer, ) - view.addView(controller.view) + + view.addView(childView) layerControllers.add(controller) } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt index ac268420fb75..3903dbaf64c6 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt @@ -53,11 +53,8 @@ class DefaultClockProvider( } return if (clockReactiveVariants) { - // TODO handle the case here where only the smallClock message buffer is added - val assetLoader = - AssetLoader(ctx, ctx, "clocks/", messageBuffers?.smallClockMessageBuffer!!) - - SimpleClockController(ctx, assetLoader, FLEX_DESIGN, messageBuffers) + val assets = AssetLoader(ctx, ctx, "clocks/", messageBuffers!!.infraMessageBuffer) + FlexClockController(ctx, resources, assets, FLEX_DESIGN, messageBuffers) } else { DefaultClockController( ctx, @@ -82,6 +79,9 @@ class DefaultClockProvider( resources.getString(R.string.clock_default_description), // TODO(b/352049256): Update placeholder to actual resource resources.getDrawable(R.drawable.clock_default_thumbnail, null), + isReactiveToTone = true, + isReactiveToTouch = clockReactiveVariants, + axes = listOf(), // TODO: Ater some picker definition ) } @@ -118,9 +118,9 @@ class DefaultClockProvider( alignment = DigitalAlignment( HorizontalAlignment.CENTER, - VerticalAlignment.CENTER + VerticalAlignment.CENTER, ), - dateTimeFormat = "hh" + dateTimeFormat = "hh", ), DigitalHandLayer( layerBounds = LayerBounds.FIT, @@ -146,9 +146,9 @@ class DefaultClockProvider( alignment = DigitalAlignment( HorizontalAlignment.CENTER, - VerticalAlignment.CENTER + VerticalAlignment.CENTER, ), - dateTimeFormat = "hh" + dateTimeFormat = "hh", ), DigitalHandLayer( layerBounds = LayerBounds.FIT, @@ -174,9 +174,9 @@ class DefaultClockProvider( alignment = DigitalAlignment( HorizontalAlignment.CENTER, - VerticalAlignment.CENTER + VerticalAlignment.CENTER, ), - dateTimeFormat = "mm" + dateTimeFormat = "mm", ), DigitalHandLayer( layerBounds = LayerBounds.FIT, @@ -202,11 +202,11 @@ class DefaultClockProvider( alignment = DigitalAlignment( HorizontalAlignment.CENTER, - VerticalAlignment.CENTER + VerticalAlignment.CENTER, ), - dateTimeFormat = "mm" - ) - ) + dateTimeFormat = "mm", + ), + ), ) ) @@ -230,7 +230,7 @@ class DefaultClockProvider( renderType = RenderType.CHANGE_WEIGHT, ), alignment = DigitalAlignment(HorizontalAlignment.LEFT, null), - dateTimeFormat = "h:mm" + dateTimeFormat = "h:mm", ) ) @@ -239,7 +239,7 @@ class DefaultClockProvider( name = "@string/clock_default_name", description = "@string/clock_default_description", large = ClockFace(layers = largeLayer), - small = ClockFace(layers = smallLayer) + small = ClockFace(layers = smallLayer), ) } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt index ec7779825bda..b8ebd0ff559b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt @@ -18,7 +18,7 @@ package com.android.systemui.shared.clocks import android.content.Context import android.content.res.Resources -import com.android.systemui.monet.Style as MonetStyle +import com.android.systemui.customization.R import com.android.systemui.plugins.clocks.AlarmData import com.android.systemui.plugins.clocks.ClockConfig import com.android.systemui.plugins.clocks.ClockController @@ -27,21 +27,24 @@ import com.android.systemui.plugins.clocks.ClockMessageBuffers import com.android.systemui.plugins.clocks.ClockReactiveSetting import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData +import com.android.systemui.shared.clocks.view.FlexClockView import java.io.PrintWriter import java.util.Locale import java.util.TimeZone -/** Controller for a simple json specified clock */ -class SimpleClockController( +/** Controller for the default flex clock */ +class FlexClockController( private val ctx: Context, - private val assets: AssetLoader, - val design: ClockDesign, + private val resources: Resources, + private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources + val design: ClockDesign, // TODO(b/364680879): Remove when done inlining val messageBuffers: ClockMessageBuffers?, ) : ClockController { override val smallClock = run { val buffer = messageBuffers?.smallClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER - SimpleClockFaceController( + FlexClockFaceController( ctx, + resources, assets.copy(messageBuffer = buffer), design.small ?: design.large!!, false, @@ -51,8 +54,9 @@ class SimpleClockController( override val largeClock = run { val buffer = messageBuffers?.largeClockMessageBuffer ?: LogUtil.DEFAULT_MESSAGE_BUFFER - SimpleClockFaceController( + FlexClockFaceController( ctx, + resources, assets.copy(messageBuffer = buffer), design.large ?: design.small!!, true, @@ -62,16 +66,10 @@ class SimpleClockController( override val config: ClockConfig by lazy { ClockConfig( - design.id, - design.name?.let { assets.tryReadString(it) ?: it } ?: "", - design.description?.let { assets.tryReadString(it) ?: it } ?: "", - isReactiveToTone = - design.colorPalette == null || design.colorPalette == MonetStyle.CLOCK, - useAlternateSmartspaceAODTransition = - smallClock.config.hasCustomWeatherDataDisplay || - largeClock.config.hasCustomWeatherDataDisplay, - useCustomClockScene = - smallClock.config.useCustomClockScene || largeClock.config.useCustomClockScene, + DEFAULT_CLOCK_ID, + resources.getString(R.string.clock_default_name), + resources.getString(R.string.clock_default_description), + isReactiveToTone = true, ) } @@ -80,8 +78,8 @@ class SimpleClockController( override var isReactiveTouchInteractionEnabled = false set(value) { field = value - smallClock.events.isReactiveTouchInteractionEnabled = value - largeClock.events.isReactiveTouchInteractionEnabled = value + val view = largeClock.view as FlexClockView + view.isReactiveTouchInteractionEnabled = value } override fun onTimeZoneChanged(timeZone: TimeZone) { 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 new file mode 100644 index 000000000000..ef24d2ad3071 --- /dev/null +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.clocks + +import android.content.Context +import android.content.res.Resources +import android.graphics.Rect +import android.view.Gravity +import android.view.View +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.widget.FrameLayout +import com.android.systemui.customization.R +import com.android.systemui.log.core.MessageBuffer +import com.android.systemui.plugins.clocks.AlarmData +import com.android.systemui.plugins.clocks.ClockAnimations +import com.android.systemui.plugins.clocks.ClockEvents +import com.android.systemui.plugins.clocks.ClockFaceConfig +import com.android.systemui.plugins.clocks.ClockFaceController +import com.android.systemui.plugins.clocks.ClockFaceEvents +import com.android.systemui.plugins.clocks.ClockFaceLayout +import com.android.systemui.plugins.clocks.ClockReactiveSetting +import com.android.systemui.plugins.clocks.DefaultClockFaceLayout +import com.android.systemui.plugins.clocks.WeatherData +import com.android.systemui.plugins.clocks.ZenData +import com.android.systemui.shared.clocks.view.FlexClockView +import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView +import java.util.Locale +import java.util.TimeZone +import kotlin.math.max + +// TODO(b/364680879): Merge w/ ComposedDigitalLayerController +class FlexClockFaceController( + ctx: Context, + private val resources: Resources, + val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources + face: ClockFace, + private val isLargeClock: Boolean, + messageBuffer: MessageBuffer, +) : ClockFaceController { + override val view: View + get() = layerController.view + + override val config = + ClockFaceConfig( + hasCustomPositionUpdatedAnimation = false // TODO(b/364673982) + ) + + private val keyguardLargeClockTopMargin = + resources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin) + val layerController: SimpleClockLayerController + val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm") + + init { + val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) + lp.gravity = Gravity.CENTER + + val layer = face.layers[0] + + layerController = + if (isLargeClock) + ComposedDigitalLayerController( + ctx, + resources, + assets, + layer as ComposedDigitalHandLayer, + messageBuffer, + ) + else { + val childView = SimpleDigitalClockTextView(ctx, messageBuffer) + SimpleDigitalHandLayerController( + ctx, + resources, + assets, + layer as DigitalHandLayer, + childView, + messageBuffer, + ) + } + layerController.view.layoutParams = lp + } + + override val layout: ClockFaceLayout = + DefaultClockFaceLayout(view).apply { + views[0].id = + if (isLargeClock) R.id.lockscreen_clock_view_large else R.id.lockscreen_clock_view + } + + override val events = FlexClockFaceEvents() + + // TODO(b/364680879): Remove ClockEvents + inner class FlexClockFaceEvents : ClockEvents, ClockFaceEvents { + override var isReactiveTouchInteractionEnabled = false + get() = field + set(value) { + field = value + layerController.events.isReactiveTouchInteractionEnabled = value + } + + override fun onTimeTick() { + timespecHandler.updateTime() + view.contentDescription = timespecHandler.getContentDescription() + layerController.faceEvents.onTimeTick() + } + + override fun onTimeZoneChanged(timeZone: TimeZone) { + timespecHandler.timeZone = timeZone + layerController.events.onTimeZoneChanged(timeZone) + } + + override fun onTimeFormatChanged(is24Hr: Boolean) { + timespecHandler.is24Hr = is24Hr + layerController.events.onTimeFormatChanged(is24Hr) + } + + override fun onLocaleChanged(locale: Locale) { + timespecHandler.updateLocale(locale) + layerController.events.onLocaleChanged(locale) + } + + override fun onFontSettingChanged(fontSizePx: Float) { + layerController.faceEvents.onFontSettingChanged(fontSizePx) + } + + override fun onColorPaletteChanged(resources: Resources) { + layerController.events.onColorPaletteChanged(resources) + layerController.updateColors() + } + + override fun onSeedColorChanged(seedColor: Int?) { + layerController.events.onSeedColorChanged(seedColor) + layerController.updateColors() + } + + override fun onRegionDarknessChanged(isRegionDark: Boolean) { + layerController.faceEvents.onRegionDarknessChanged(isRegionDark) + } + + override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} + + /** + * targetRegion passed to all customized clock applies counter translationY of + * KeyguardStatusView and keyguard_large_clock_top_margin from default clock + */ + override fun onTargetRegionChanged(targetRegion: Rect?) { + // When a clock needs to be aligned with screen, like weather clock + // it needs to offset back the translation of keyguard_large_clock_top_margin + if (isLargeClock && (view as FlexClockView).isAlignedWithScreen()) { + val topMargin = keyguardLargeClockTopMargin + targetRegion?.let { + val (_, yDiff) = computeLayoutDiff(view, it, isLargeClock) + // In LS, we use yDiff to counter translate + // the translation of KeyguardLargeClockTopMargin + // With the targetRegion passed from picker, + // we will have yDiff = 0, no translation is needed for weather clock + if (yDiff.toInt() != 0) view.translationY = yDiff - topMargin / 2 + } + return + } + + var maxWidth = 0f + var maxHeight = 0f + + layerController.faceEvents.onTargetRegionChanged(targetRegion) + maxWidth = max(maxWidth, view.layoutParams.width.toFloat()) + maxHeight = max(maxHeight, view.layoutParams.height.toFloat()) + + val lp = + if (maxHeight <= 0 || maxWidth <= 0 || targetRegion == null) { + // No specified width/height. Just match parent size. + FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) + } else { + // Scale to fit in targetRegion based on largest child elements. + val ratio = maxWidth / maxHeight + val targetRatio = targetRegion.width() / targetRegion.height().toFloat() + val scale = + if (ratio > targetRatio) targetRegion.width() / maxWidth + else targetRegion.height() / maxHeight + + FrameLayout.LayoutParams( + (maxWidth * scale).toInt(), + (maxHeight * scale).toInt(), + ) + } + + lp.gravity = Gravity.CENTER + view.layoutParams = lp + targetRegion?.let { + val (xDiff, yDiff) = computeLayoutDiff(view, it, isLargeClock) + view.translationX = xDiff + view.translationY = yDiff + } + } + + override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {} + + override fun onWeatherDataChanged(data: WeatherData) { + layerController.events.onWeatherDataChanged(data) + } + + override fun onAlarmDataChanged(data: AlarmData) { + layerController.events.onAlarmDataChanged(data) + } + + override fun onZenDataChanged(data: ZenData) { + layerController.events.onZenDataChanged(data) + } + } + + override val animations = + object : ClockAnimations { + override fun enter() { + layerController.animations.enter() + } + + override fun doze(fraction: Float) { + layerController.animations.doze(fraction) + } + + override fun fold(fraction: Float) { + layerController.animations.fold(fraction) + } + + override fun charge() { + layerController.animations.charge() + } + + override fun onPickerCarouselSwiping(swipingFraction: Float) { + face.pickerScale?.let { + view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX + view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY + } + if (isLargeClock && !(view as FlexClockView).isAlignedWithScreen()) { + view.translationY = keyguardLargeClockTopMargin / 2F * swipingFraction + } + layerController.animations.onPickerCarouselSwiping(swipingFraction) + view.invalidate() + } + + override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) { + layerController.animations.onPositionUpdated(fromLeft, direction, fraction) + } + + override fun onPositionUpdated(distance: Float, fraction: Float) { + layerController.animations.onPositionUpdated(distance, fraction) + } + } +} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt deleted file mode 100644 index ef398d1a52a0..000000000000 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockFaceController.kt +++ /dev/null @@ -1,314 +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.shared.clocks - -import android.content.Context -import android.content.res.Resources -import android.graphics.Rect -import android.view.Gravity -import android.view.View -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.widget.FrameLayout -import com.android.systemui.log.core.MessageBuffer -import com.android.systemui.plugins.clocks.AlarmData -import com.android.systemui.plugins.clocks.ClockAnimations -import com.android.systemui.plugins.clocks.ClockEvents -import com.android.systemui.plugins.clocks.ClockFaceConfig -import com.android.systemui.plugins.clocks.ClockFaceController -import com.android.systemui.plugins.clocks.ClockFaceEvents -import com.android.systemui.plugins.clocks.ClockFaceLayout -import com.android.systemui.plugins.clocks.ClockReactiveSetting -import com.android.systemui.plugins.clocks.ClockTickRate -import com.android.systemui.plugins.clocks.DefaultClockFaceLayout -import com.android.systemui.plugins.clocks.WeatherData -import com.android.systemui.plugins.clocks.ZenData -import com.android.systemui.shared.clocks.view.DigitalClockFaceView -import java.util.Locale -import java.util.TimeZone -import kotlin.math.max - -interface ClockEventUnion : ClockEvents, ClockFaceEvents - -class SimpleClockFaceController( - ctx: Context, - val assets: AssetLoader, - face: ClockFace, - isLargeClock: Boolean, - messageBuffer: MessageBuffer, -) : ClockFaceController { - override val view: View - override val config: ClockFaceConfig by lazy { - ClockFaceConfig( - hasCustomWeatherDataDisplay = layers.any { it.config.hasCustomWeatherDataDisplay }, - hasCustomPositionUpdatedAnimation = - layers.any { it.config.hasCustomPositionUpdatedAnimation }, - tickRate = getTickRate(), - useCustomClockScene = layers.any { it.config.useCustomClockScene }, - ) - } - - val layers = mutableListOf<SimpleClockLayerController>() - - val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm") - - init { - val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) - lp.gravity = Gravity.CENTER - view = - if (face.layers.size == 1) { - // Optimize a clocks with a single layer by excluding the face level view group. We - // expect the view container from the host process to always be a FrameLayout. - val layer = face.layers[0] - val controller = - SimpleClockLayerController.Factory.create( - ctx, - assets, - layer, - isLargeClock, - messageBuffer, - ) - layers.add(controller) - controller.view.layoutParams = lp - controller.view - } else { - // For multiple views, we use an intermediate RelativeLayout so that we can do some - // intelligent laying out between the children views. - val group = SimpleClockRelativeLayout(ctx, face.faceLayout) - group.layoutParams = lp - group.gravity = Gravity.CENTER - group.clipChildren = false - for (layer in face.layers) { - face.faceLayout?.let { - if (layer is DigitalHandLayer) { - layer.faceLayout = it - } - } - val controller = - SimpleClockLayerController.Factory.create( - ctx, - assets, - layer, - isLargeClock, - messageBuffer, - ) - group.addView(controller.view) - layers.add(controller) - } - group - } - } - - override val layout: ClockFaceLayout = - DefaultClockFaceLayout(view).apply { - views[0].id = - if (isLargeClock) { - assets.getResourcesId("lockscreen_clock_view_large") - } else { - assets.getResourcesId("lockscreen_clock_view") - } - } - - override val events = - object : ClockEventUnion { - override var isReactiveTouchInteractionEnabled = false - get() = field - set(value) { - field = value - layers.forEach { it.events.isReactiveTouchInteractionEnabled = value } - } - - override fun onTimeTick() { - timespecHandler.updateTime() - if ( - config.tickRate == ClockTickRate.PER_MINUTE || - view.contentDescription != timespecHandler.getContentDescription() - ) { - view.contentDescription = timespecHandler.getContentDescription() - } - layers.forEach { it.faceEvents.onTimeTick() } - } - - override fun onTimeZoneChanged(timeZone: TimeZone) { - timespecHandler.timeZone = timeZone - layers.forEach { it.events.onTimeZoneChanged(timeZone) } - } - - override fun onTimeFormatChanged(is24Hr: Boolean) { - timespecHandler.is24Hr = is24Hr - layers.forEach { it.events.onTimeFormatChanged(is24Hr) } - } - - override fun onLocaleChanged(locale: Locale) { - timespecHandler.updateLocale(locale) - layers.forEach { it.events.onLocaleChanged(locale) } - } - - override fun onFontSettingChanged(fontSizePx: Float) { - layers.forEach { it.faceEvents.onFontSettingChanged(fontSizePx) } - } - - override fun onColorPaletteChanged(resources: Resources) { - layers.forEach { - it.events.onColorPaletteChanged(resources) - it.updateColors() - } - } - - override fun onSeedColorChanged(seedColor: Int?) { - layers.forEach { - it.events.onSeedColorChanged(seedColor) - it.updateColors() - } - } - - override fun onRegionDarknessChanged(isRegionDark: Boolean) { - layers.forEach { it.faceEvents.onRegionDarknessChanged(isRegionDark) } - } - - override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {} - - /** - * targetRegion passed to all customized clock applies counter translationY of - * KeyguardStatusView and keyguard_large_clock_top_margin from default clock - */ - override fun onTargetRegionChanged(targetRegion: Rect?) { - // When a clock needs to be aligned with screen, like weather clock - // it needs to offset back the translation of keyguard_large_clock_top_margin - if (view is DigitalClockFaceView && view.isAlignedWithScreen()) { - val topMargin = getKeyguardLargeClockTopMargin(assets) - targetRegion?.let { - val (_, yDiff) = computeLayoutDiff(view, it, isLargeClock) - // In LS, we use yDiff to counter translate - // the translation of KeyguardLargeClockTopMargin - // With the targetRegion passed from picker, - // we will have yDiff = 0, no translation is needed for weather clock - if (yDiff.toInt() != 0) view.translationY = yDiff - topMargin / 2 - } - return - } - - var maxWidth = 0f - var maxHeight = 0f - - for (layer in layers) { - layer.faceEvents.onTargetRegionChanged(targetRegion) - maxWidth = max(maxWidth, layer.view.layoutParams.width.toFloat()) - maxHeight = max(maxHeight, layer.view.layoutParams.height.toFloat()) - } - - val lp = - if (maxHeight <= 0 || maxWidth <= 0 || targetRegion == null) { - // No specified width/height. Just match parent size. - FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) - } else { - // Scale to fit in targetRegion based on largest child elements. - val ratio = maxWidth / maxHeight - val targetRatio = targetRegion.width() / targetRegion.height().toFloat() - val scale = - if (ratio > targetRatio) targetRegion.width() / maxWidth - else targetRegion.height() / maxHeight - - FrameLayout.LayoutParams( - (maxWidth * scale).toInt(), - (maxHeight * scale).toInt(), - ) - } - - lp.gravity = Gravity.CENTER - view.layoutParams = lp - targetRegion?.let { - val (xDiff, yDiff) = computeLayoutDiff(view, it, isLargeClock) - view.translationX = xDiff - view.translationY = yDiff - } - } - - override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) {} - - override fun onWeatherDataChanged(data: WeatherData) { - layers.forEach { it.events.onWeatherDataChanged(data) } - } - - override fun onAlarmDataChanged(data: AlarmData) { - layers.forEach { it.events.onAlarmDataChanged(data) } - } - - override fun onZenDataChanged(data: ZenData) { - layers.forEach { it.events.onZenDataChanged(data) } - } - } - - override val animations = - object : ClockAnimations { - override fun enter() { - layers.forEach { it.animations.enter() } - } - - override fun doze(fraction: Float) { - layers.forEach { it.animations.doze(fraction) } - } - - override fun fold(fraction: Float) { - layers.forEach { it.animations.fold(fraction) } - } - - override fun charge() { - layers.forEach { it.animations.charge() } - } - - override fun onPickerCarouselSwiping(swipingFraction: Float) { - face.pickerScale?.let { - view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX - view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY - } - if (!(view is DigitalClockFaceView && view.isAlignedWithScreen())) { - val topMargin = getKeyguardLargeClockTopMargin(assets) - view.translationY = topMargin / 2F * swipingFraction - } - layers.forEach { it.animations.onPickerCarouselSwiping(swipingFraction) } - view.invalidate() - } - - override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) { - layers.forEach { it.animations.onPositionUpdated(fromLeft, direction, fraction) } - } - - override fun onPositionUpdated(distance: Float, fraction: Float) { - layers.forEach { it.animations.onPositionUpdated(distance, fraction) } - } - } - - private fun getTickRate(): ClockTickRate { - var tickRate = ClockTickRate.PER_MINUTE - for (layer in layers) { - if (layer.config.tickRate.value < tickRate.value) { - tickRate = layer.config.tickRate - } - } - return tickRate - } - - private fun getKeyguardLargeClockTopMargin(assets: AssetLoader): Int { - val topMarginRes = - assets.resolveResourceId(null, "dimen", "keyguard_large_clock_top_margin") - if (topMarginRes != null) { - val (res, id) = topMarginRes - return res.getDimensionPixelSize(id) - } - return 0 - } -} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt index f71543efa650..5d1a2dbc4209 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockLayerController.kt @@ -16,25 +16,12 @@ package com.android.systemui.shared.clocks -import android.content.Context import android.view.View import androidx.annotation.VisibleForTesting -import com.android.systemui.log.core.MessageBuffer import com.android.systemui.plugins.clocks.ClockAnimations import com.android.systemui.plugins.clocks.ClockEvents import com.android.systemui.plugins.clocks.ClockFaceConfig import com.android.systemui.plugins.clocks.ClockFaceEvents -import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView -import kotlin.reflect.KClass - -typealias LayerControllerConstructor = - ( - ctx: Context, - assets: AssetLoader, - layer: ClockLayer, - isLargeClock: Boolean, - messageBuffer: MessageBuffer, - ) -> SimpleClockLayerController interface SimpleClockLayerController { val view: View @@ -48,55 +35,4 @@ interface SimpleClockLayerController { // Called immediately after either onColorPaletteChanged or onSeedColorChanged is called. // Provided for convience to not duplicate color update logic after state updated. fun updateColors() {} - - companion object Factory { - val constructorMap = mutableMapOf<Pair<KClass<*>, KClass<*>?>, LayerControllerConstructor>() - - internal inline fun <reified TLayer> registerConstructor( - noinline constructor: LayerControllerConstructor, - ) where TLayer : ClockLayer { - constructorMap[Pair(TLayer::class, null)] = constructor - } - - inline fun <reified TLayer, reified TStyle> registerTextConstructor( - noinline constructor: LayerControllerConstructor, - ) where TLayer : ClockLayer, TStyle : TextStyle { - constructorMap[Pair(TLayer::class, TStyle::class)] = constructor - } - - init { - registerConstructor<ComposedDigitalHandLayer>(::ComposedDigitalLayerController) - registerTextConstructor<DigitalHandLayer, FontTextStyle>(::createSimpleDigitalLayer) - } - - private fun createSimpleDigitalLayer( - ctx: Context, - assets: AssetLoader, - layer: ClockLayer, - isLargeClock: Boolean, - messageBuffer: MessageBuffer - ): SimpleClockLayerController { - val view = SimpleDigitalClockTextView(ctx, messageBuffer) - return SimpleDigitalHandLayerController( - ctx, - assets, - layer as DigitalHandLayer, - view, - messageBuffer - ) - } - - fun create( - ctx: Context, - assets: AssetLoader, - layer: ClockLayer, - isLargeClock: Boolean, - messageBuffer: MessageBuffer - ): SimpleClockLayerController { - val styleClass = if (layer is DigitalHandLayer) layer.style::class else null - val key = Pair(layer::class, styleClass) - return constructorMap[key]?.invoke(ctx, assets, layer, isLargeClock, messageBuffer) - ?: throw IllegalArgumentException("Unrecognized ClockLayer type: $key") - } - } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt index a3240f81e499..ce1eae48546a 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt @@ -42,7 +42,8 @@ private val TAG = SimpleDigitalHandLayerController::class.simpleName!! open class SimpleDigitalHandLayerController<T>( private val ctx: Context, - private val assets: AssetLoader, + private val resources: Resources, + private val assets: AssetLoader, // TODO(b/364680879): Remove and replace w/ resources private val layer: DigitalHandLayer, override val view: T, messageBuffer: MessageBuffer, @@ -68,7 +69,7 @@ open class SimpleDigitalHandLayerController<T>( view.layoutParams = RelativeLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT + ViewGroup.LayoutParams.WRAP_CONTENT, ) if (layer.alignment != null) { layer.alignment.verticalAlignment?.let { view.verticalAlignment = it } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt index eb7234646a64..81efcb9de4d8 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt @@ -31,6 +31,7 @@ import com.android.systemui.shared.clocks.AssetLoader import com.android.systemui.shared.clocks.LogUtil import java.util.Locale +// TODO(b/364680879): Merge w/ only subclass FlexClockView abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer) : FrameLayout(ctx) { protected val logger = Logger(messageBuffer, this::class.simpleName!!) get() = field ?: LogUtil.FALLBACK_INIT_LOGGER @@ -140,7 +141,6 @@ abstract class DigitalClockFaceView(ctx: Context, messageBuffer: MessageBuffer) open val useCustomClockScene get() = false - // TODO: implement ClockEventUnion? open fun onLocaleChanged(locale: Locale) {} open fun onWeatherDataChanged(data: WeatherData) {} diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt index c29c8dac8ba6..25b2ad772b32 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt @@ -35,7 +35,7 @@ import kotlin.math.min fun clamp(value: Float, minVal: Float, maxVal: Float): Float = max(min(value, maxVal), minVal) -class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffer: MessageBuffer) : +class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: MessageBuffer) : DigitalClockFaceView(context, messageBuffer) { override var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>() val digitLeftTopMap = mutableMapOf<Int, Point>() @@ -57,11 +57,9 @@ class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffe private var prevY = 0f private var isDown = false - // TODO(b/340253296): Genericize; json spec private var wght = 603f private var wdth = 100f - // TODO(b/340253296): Json spec private val MAX_WGHT = 950f private val MIN_WGHT = 50f private val WGHT_SCALE = 0.5f @@ -71,7 +69,6 @@ class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffe private val WDTH_SCALE = 0.2f override fun onTouchEvent(evt: MotionEvent): Boolean { - // TODO(b/340253296): implement on DigitalClockFaceView? if (!isReactiveTouchInteractionEnabled) { return super.onTouchEvent(evt) } @@ -94,12 +91,11 @@ class FlexClockView(context: Context, val assetLoader: AssetLoader, messageBuffe prevX = evt.x prevY = evt.y - // TODO(b/340253296): Genericize; json spec val fvar = "'wght' $wght, 'wdth' $wdth, 'opsz' 144, 'ROND' 100" digitalClockTextViewMap.forEach { (_, view) -> val textStyle = view.textStyle as FontTextStyle textStyle.fontVariation = fvar - view.applyStyles(assetLoader, textStyle, view.aodStyle) + view.applyStyles(assets, textStyle, view.aodStyle) } requestLayout() diff --git a/packages/SystemUI/docs/scene.md b/packages/SystemUI/docs/scene.md index 234c7a032d2e..bf15b4feadfb 100644 --- a/packages/SystemUI/docs/scene.md +++ b/packages/SystemUI/docs/scene.md @@ -63,7 +63,7 @@ the instructions below to turn it on. NOTE: in case these instructions become stale and don't actually enable the framework, please make sure `SceneContainerFlag.isEnabled` in the [`SceneContainerFlag.kt`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt) -file evalutes to `true`. +file evaluates to `true`. 1. Set a collection of **aconfig flags** to `true` by running the following commands: @@ -73,7 +73,6 @@ file evalutes to `true`. $ adb shell device_config override systemui com.android.systemui.migrate_clocks_to_blueprint true $ adb shell device_config override systemui com.android.systemui.notification_avalanche_throttle_hun true $ adb shell device_config override systemui com.android.systemui.predictive_back_sysui true - $ adb shell device_config override systemui com.android.systemui.device_entry_udfps_refactor true $ adb shell device_config override systemui com.android.systemui.scene_container true ``` 2. **Restart** System UI by issuing the following command: diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index 2bb9e68a357a..00c5577b8017 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -156,8 +156,12 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility)) .thenReturn(INVISIBLE); - when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame); - when(mView.findViewById(R.id.lockscreen_clock_view)).thenReturn(mSmallClockFrame); + when(mView + .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large)) + .thenReturn(mLargeClockFrame); + when(mView + .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view)) + .thenReturn(mSmallClockFrame); when(mSmallClockView.getContext()).thenReturn(getContext()); when(mLargeClockView.getContext()).thenReturn(getContext()); diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 0bf9d12a09d5..4ed5fd0a6e71 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -113,8 +113,10 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { }); mKeyguardClockSwitch = (KeyguardClockSwitch) layoutInflater.inflate(R.layout.keyguard_clock_switch, null); - mSmallClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view); - mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large); + mSmallClockFrame = mKeyguardClockSwitch + .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view); + mLargeClockFrame = mKeyguardClockSwitch + .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large); mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area); mKeyguardClockSwitch.mChildrenAreLaidOut = true; } diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt index 2e41246a62a1..245388c214a5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt @@ -17,10 +17,10 @@ package com.android.keyguard import android.view.View -import android.view.ViewGroup import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -88,7 +88,7 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { underTest.statusViewCentered = true val view = View(context) - whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn( view ) @@ -110,7 +110,7 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(SHADE) val view = View(context) - whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + whenever(keyguardRootView.findViewById<View>(customR.id.lockscreen_clock_view_large)).thenReturn( view ) @@ -134,7 +134,7 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { val view = View(context) whenever( notificationShadeWindowView - .findViewById<View>(R.id.lockscreen_clock_view_large) + .findViewById<View>(customR.id.lockscreen_clock_view_large) ).thenReturn(view) progressListener.onTransitionStarted() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt deleted file mode 100644 index 13306becf6d2..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsBpViewControllerTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager -import org.junit.Assert.assertFalse -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 - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper -class UdfpsBpViewControllerTest : SysuiTestCase() { - - @JvmField @Rule var rule = MockitoJUnit.rule() - - @Mock lateinit var udfpsBpView: UdfpsBpView - @Mock lateinit var statusBarStateController: StatusBarStateController - @Mock lateinit var shadeInteractor: ShadeInteractor - @Mock lateinit var systemUIDialogManager: SystemUIDialogManager - @Mock lateinit var dumpManager: DumpManager - @Mock lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor - - private lateinit var udfpsBpViewController: UdfpsBpViewController - - @Before - fun setup() { - udfpsBpViewController = - UdfpsBpViewController( - udfpsBpView, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - - @TestableLooper.RunWithLooper(setAsMainLooper = true) - @Test - fun testShouldNeverPauseAuth() { - assertFalse(udfpsBpViewController.shouldPauseAuth()) - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 437a4b357372..21c6583d4e84 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -16,34 +16,17 @@ package com.android.systemui.biometrics; -import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; -import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; -import static android.view.MotionEvent.ACTION_DOWN; -import static android.view.MotionEvent.ACTION_MOVE; -import static android.view.MotionEvent.ACTION_UP; - -import static com.android.internal.util.FunctionalUtils.ThrowingConsumer; -import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.graphics.Rect; -import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricRequestConstants; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; @@ -58,13 +41,9 @@ import android.os.Handler; import android.os.PowerManager; import android.os.RemoteException; import android.testing.TestableLooper.RunWithLooper; -import android.util.Pair; -import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.Surface; import android.view.View; -import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.accessibility.AccessibilityManager; @@ -75,15 +54,11 @@ import com.android.app.viewcapture.ViewCaptureAwareWindowManager; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams; -import com.android.systemui.biometrics.udfps.InteractionEvent; -import com.android.systemui.biometrics.udfps.NormalizedTouchData; import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor; -import com.android.systemui.biometrics.udfps.TouchProcessorResult; import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayViewModel; import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -102,7 +77,6 @@ import com.android.systemui.power.data.repository.FakePowerRepository; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.shared.model.WakeSleepReason; import com.android.systemui.power.shared.model.WakefulnessState; -import com.android.systemui.res.R; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; @@ -130,7 +104,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -204,16 +177,6 @@ public class UdfpsControllerTest extends SysuiTestCase { private FeatureFlags mFeatureFlags; // Stuff for configuring mocks @Mock - private UdfpsView mUdfpsView; - @Mock - private UdfpsBpView mBpView; - @Mock - private UdfpsFpmEmptyView mFpmEmptyView; - @Mock - private UdfpsKeyguardViewLegacy mKeyguardView; - private final UdfpsAnimationViewController mUdfpsKeyguardViewController = - mock(UdfpsKeyguardViewControllerLegacy.class); - @Mock private SystemUIDialogManager mSystemUIDialogManager; @Mock private ActivityTransitionAnimator mActivityTransitionAnimator; @@ -241,8 +204,6 @@ public class UdfpsControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<View> mViewCaptor; @Captor - private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor; - @Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor; @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor; @@ -287,18 +248,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mContext.getOrCreateTestableResources() .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false); - when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)) - .thenReturn(mUdfpsView); - when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD - when(mLayoutInflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mBpView); - when(mLayoutInflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mFpmEmptyView); when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); when(mSessionTracker.getSessionId(anyInt())).thenReturn( (new InstanceIdSequence(1 << 20)).newInstanceId()); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */, @@ -327,7 +279,6 @@ public class UdfpsControllerTest extends SysuiTestCase { // Create a fake background executor. mBiometricExecutor = new FakeExecutor(new FakeSystemClock()); - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); initUdfpsController(mOpticalProps); } @@ -349,7 +300,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mFalsingManager, mPowerManager, mAccessibilityManager, - mLockscreenShadeTransitionController, mScreenLifecycle, mVibrator, mUdfpsHapticsSimulator, @@ -390,101 +340,6 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void dozeTimeTick() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - mUdfpsController.dozeTimeTick(); - verify(mUdfpsView).dozeTimeTick(); - } - - @Test - public void onActionDownTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = obtainMotionEvent(ACTION_DOWN, 0, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { - onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */); - } - - @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale() - throws RemoteException { - onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */); - } - - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale) - throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_MOVE is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - if (stale) { - mOverlayController.hideUdfpsOverlay(mOpticalProps.sensorId); - mFgExecutor.runAllReady(); - } - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager, stale ? never() : times(1)) - .notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test - public void onMultipleTouch_whenCanDismissLockScreen_entersDeviceOnce() throws RemoteException { - // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - - // GIVEN that the overlay is showing - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); - } - - @Test public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { // GIVEN overlay was showing and the udfps bouncer is showing mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, @@ -524,61 +379,6 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception { - final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)}; - final int[] displayWidth = new int[]{1080, 1440}; - final int[] displayHeight = new int[]{1920, 2560}; - final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]}; - final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90}; - final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0], - sensorBounds[0], displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0], - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); - - for (int i1 = 0; i1 <= 1; ++i1) { - for (int i2 = 0; i2 <= 1; ++i2) { - for (int i3 = 0; i3 <= 1; ++i3) { - for (int i4 = 0; i4 <= 1; ++i4) { - for (int i5 = 0; i5 <= 1; ++i5) { - final UdfpsOverlayParams newParams = new UdfpsOverlayParams( - sensorBounds[i1], sensorBounds[i1], displayWidth[i2], - displayHeight[i3], scaleFactor[i4], rotation[i5], - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); - - if (newParams.equals(oldParams)) { - continue; - } - - // Initialize the overlay with old parameters. - mUdfpsController.updateOverlayParams(mOpticalProps, oldParams); - - // Show the overlay. - reset(mWindowManager); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, - mOpticalProps.sensorId, - BiometricRequestConstants.REASON_ENROLL_ENROLLING, - mUdfpsOverlayControllerCallback); - - mFgExecutor.runAllReady(); - verify(mWindowManager).addView(mViewCaptor.capture(), any()); - when(mViewCaptor.getValue().getParent()) - .thenReturn(mock(ViewGroup.class)); - - // Update overlay parameters. - reset(mWindowManager); - mUdfpsController.updateOverlayParams(mOpticalProps, newParams); - mFgExecutor.runAllReady(); - - // Ensure the overlay was recreated. - verify(mWindowManager).removeView(any()); - verify(mWindowManager).addView(any(), any()); - } - } - } - } - } - } - - @Test public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception { final Rect sensorBounds = new Rect(10, 10, 20, 20); final int displayWidth = 1080; @@ -595,7 +395,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); - verify(mWindowManager).addView(any(), any()); // Update overlay with the same parameters. mUdfpsController.updateOverlayParams(mOpticalProps, @@ -606,870 +405,4 @@ public class UdfpsControllerTest extends SysuiTestCase { // Ensure the overlay was not recreated. verify(mWindowManager, never()).removeView(any()); } - - private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor, - float major) { - MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties(); - pp.id = 1; - MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords(); - pc.x = x; - pc.y = y; - pc.touchMinor = minor; - pc.touchMajor = major; - return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp}, - new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0); - } - - private static class TestParams { - public final FingerprintSensorPropertiesInternal sensorProps; - - TestParams(FingerprintSensorPropertiesInternal sensorProps) { - this.sensorProps = sensorProps; - } - } - - private void runWithAllParams(ThrowingConsumer<TestParams> testParamsConsumer) { - for (FingerprintSensorPropertiesInternal sensorProps : List.of(mOpticalProps, - mUltrasonicProps)) { - initUdfpsController(sensorProps); - testParamsConsumer.accept(new TestParams(sensorProps)); - } - } - - @Test - public void onTouch_propagatesTouchInNativeOrientationAndResolution() { - runWithAllParams( - this::onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized); - } - - private void onTouch_propagatesTouchInNativeOrientationAndResolutionParameterized( - TestParams testParams) throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner. - final int pointerId = 0; - final int displayWidth = 1080; - final int displayHeight = 1920; - final float scaleFactor = 1f; // This means the native resolution is 1440x2560. - final float touchMinor = 10f; - final float touchMajor = 20f; - final float orientation = 30f; - - // Expecting a touch at the very bottom right corner in native orientation and resolution. - final float expectedX = displayWidth / scaleFactor; - final float expectedY = displayHeight / scaleFactor; - final float expectedMinor = touchMinor / scaleFactor; - final float expectedMajor = touchMajor / scaleFactor; - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // GIVEN a valid touch on sensor - NormalizedTouchData touchData = new NormalizedTouchData(pointerId, displayWidth, - displayHeight, touchMinor, touchMajor, orientation, 0L, 0L); - TouchProcessorResult processorDownResult = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1, touchData); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorDownResult); - - // Show the overlay. - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // Test ROTATION_0 - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_0, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, - touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_90 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_90, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_270 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_270, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - - // Test ROTATION_180 - reset(mFingerprintManager); - mUdfpsController.updateOverlayParams(testParams.sensorProps, - new UdfpsOverlayParams(sensorBounds, sensorBounds, displayWidth, displayHeight, - scaleFactor, Surface.ROTATION_180, - FingerprintSensorProperties.TYPE_UDFPS_OPTICAL)); - // ROTATION_180 is not supported. It should be treated like ROTATION_0. - event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId), eq(pointerId), eq(expectedX), eq(expectedY), - eq(expectedMinor), eq(expectedMajor), eq(orientation), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void fingerDown() { - runWithAllParams(this::fingerDownParameterized); - } - - private void fingerDownParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView, mFingerprintManager, mLatencyTracker, - mKeyguardUpdateMonitor); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.DOWN, 1 /* pointerId */, touchData); - - initUdfpsController(testParams.sensorProps); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // WHEN ACTION_DOWN is received - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch provider is notified about onPointerDown. - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - - // AND display configuration begins - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - } else { - verify(mLatencyTracker, never()).onActionStart( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mUdfpsView, never()).configureDisplay(any()); - } - verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // AND onDisplayConfigured notifies FingerprintManager about onUiReady - mOnDisplayConfiguredCaptor.getValue().run(); - mBiometricExecutor.runAllReady(); - InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID), - eq(testParams.sensorProps.sensorId)); - inOrder.verify(mLatencyTracker).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - } else { - verify(mFingerprintManager, never()).onUdfpsUiEvent( - eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); - verify(mLatencyTracker, never()).onActionEnd( - eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - } - } - - @Test - public void aodInterrupt() { - runWithAllParams(this::aodInterruptParameterized); - } - - private void aodInterruptParameterized(TestParams testParams) throws RemoteException { - mUdfpsController.cancelAodSendFingerUpAction(); - reset(mUdfpsView, mFingerprintManager, mKeyguardUpdateMonitor); - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // GIVEN that the overlay is showing and screen is on and fp is running - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - // WHEN fingerprint is requested because of AOD interrupt - mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN display configuration begins - // AND onDisplayConfigured notifies FingerprintManager about onUiReady - verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - mOnDisplayConfiguredCaptor.getValue().run(); - } else { - verify(mUdfpsView, never()).configureDisplay(mOnDisplayConfiguredCaptor.capture()); - } - mBiometricExecutor.runAllReady(); - - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean()); - } - - @Test - public void tryAodSendFingerUp_displayConfigurationChanges() { - runWithAllParams(this::cancelAodInterruptParameterized); - } - - private void cancelAodInterruptParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - // WHEN it is cancelled - mUdfpsController.tryAodSendFingerUp(); - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - // WHEN it is cancelled - mUdfpsController.tryAodSendFingerUp(); - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void onFingerUp_displayConfigurationChange() { - runWithAllParams(this::onFingerUp_displayConfigurationParameterized); - } - - private void onFingerUp_displayConfigurationParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // GIVEN AOD interrupt - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - - // WHEN up-action received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - mFgExecutor.runAllReady(); - - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // WHEN up-action received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - mFgExecutor.runAllReady(); - - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void onAcquiredGood_displayConfigurationChange() { - runWithAllParams(this::onAcquiredGood_displayConfigurationParameterized); - } - - private void onAcquiredGood_displayConfigurationParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - - // GIVEN overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - // WHEN ACQUIRED_GOOD received - mOverlayController.onAcquired(testParams.sensorProps.sensorId, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - // THEN the display is unconfigured - verify(mUdfpsView).unconfigureDisplay(); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - // WHEN ACQUIRED_GOOD received - mOverlayController.onAcquired(testParams.sensorProps.sensorId, - BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptTimeout() { - runWithAllParams(this::aodInterruptTimeoutParameterized); - } - - private void aodInterruptTimeoutParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - // WHEN it times out - mFgExecutor.advanceClockToNext(); - mFgExecutor.runAllReady(); - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN the display is unconfigured. - verify(mUdfpsView).unconfigureDisplay(); - } else { - // THEN the display configuration is unchanged. - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptCancelTimeoutActionOnFingerUp() { - runWithAllParams(this::aodInterruptCancelTimeoutActionOnFingerUpParameterized); - } - - private void aodInterruptCancelTimeoutActionOnFingerUpParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - when(mUdfpsView.getViewRootImpl()).thenReturn(mViewRootImpl); - - // GIVEN AOD interrupt - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - mFgExecutor.runAllReady(); - - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // Configure UdfpsView to accept the ACTION_UP event - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - - // WHEN ACTION_UP is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent); - mBiometricExecutor.runAllReady(); - upEvent.recycle(); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - mFgExecutor.runAllReady(); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // Configure UdfpsView to accept the finger up event - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - } else { - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - } - - // WHEN it times out - mFgExecutor.advanceClockToNext(); - mFgExecutor.runAllReady(); - - if (testParams.sensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL) { - // THEN the display should be unconfigured once. If the timeout action is not - // cancelled, the display would be unconfigured twice which would cause two - // FP attempts. - verify(mUdfpsView).unconfigureDisplay(); - } else { - verify(mUdfpsView, never()).unconfigureDisplay(); - } - } - - @Test - public void aodInterruptScreenOff() { - runWithAllParams(this::aodInterruptScreenOffParameterized); - } - - private void aodInterruptScreenOffParameterized(TestParams testParams) throws RemoteException { - reset(mUdfpsView); - - // GIVEN screen off - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOff(); - mFgExecutor.runAllReady(); - - // WHEN aod interrupt is received - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - - // THEN display doesn't get configured because it's off - verify(mUdfpsView, never()).configureDisplay(any()); - } - - @Test - public void aodInterrupt_fingerprintNotRunning() { - runWithAllParams(this::aodInterrupt_fingerprintNotRunningParameterized); - } - - private void aodInterrupt_fingerprintNotRunningParameterized(TestParams testParams) - throws RemoteException { - reset(mUdfpsView); - - // GIVEN showing overlay - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, testParams.sensorProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mScreenObserver.onScreenTurnedOn(); - mFgExecutor.runAllReady(); - - // WHEN aod interrupt is received when the fingerprint service isn't running - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); - mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); - - // THEN display doesn't get configured because it's off - verify(mUdfpsView, never()).configureDisplay(any()); - } - - @Test - public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, true); - - // WHEN ACTION_HOVER is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); - MotionEvent enterEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0); - mHoverListenerCaptor.getValue().onHover(mUdfpsView, enterEvent); - enterEvent.recycle(); - - // THEN context click haptic is played - verify(mVibrator).performHapticFeedback( - any(), - eq(HapticFeedbackConstants.CONTEXT_CLICK) - ); - } - - @Test - public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN NO haptic played - verify(mVibrator, never()).performHapticFeedback(any(), anyInt()); - } - - @Test - public void fingerDown_falsingManagerInformed() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN falsing manager is informed of the touch - verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION); - } - - private Pair<TouchProcessorResult, TouchProcessorResult> givenFingerEvent( - InteractionEvent event1, InteractionEvent event2, boolean a11y) - throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - event1, 1 /* pointerId */, touchData); - final TouchProcessorResult processorResultUp = new TouchProcessorResult.ProcessedTouch( - event2, 1 /* pointerId */, touchData); - - // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider. - initUdfpsController(mOpticalProps); - - // Configure UdfpsView to accept the ACTION_DOWN event - when(mUdfpsView.isDisplayConfigured()).thenReturn(false); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(a11y); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - if (a11y) { - verify(mUdfpsView).setOnHoverListener(mHoverListenerCaptor.capture()); - } else { - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - } - - return new Pair<>(processorResultDown, processorResultUp); - } - - @Test - public void onTouch_forwardToKeyguard() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch( - InteractionEvent.UNCHANGED, -1 /* pointerOnSensorId */, touchData); - - // GIVEN that the overlay is showing and a11y touch exploration NOT enabled - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - - // THEN the touch is forwarded to Keyguard - verify(mStatusBarKeyguardViewManager).onTouch(downEvent); - } - - @Test - public void onTouch_pilferPointer() throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UNCHANGED, false); - - // WHEN ACTION_DOWN is received - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent event = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - - // WHEN ACTION_MOVE is received after - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.second); - event.setAction(ACTION_MOVE); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event); - mBiometricExecutor.runAllReady(); - event.recycle(); - - // THEN only pilfer once on the initial down - verify(mInputManager).pilferPointers(any()); - } - - @Test - public void onTouch_doNotPilferPointer() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultUnchanged = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.UNCHANGED, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received and touch is not within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultUnchanged); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch is NOT pilfered - verify(mInputManager, never()).pilferPointers(any()); - } - - @Test - public void onDownTouchReceivedWithoutPreviousUp() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN ACTION_DOWN is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); - mBiometricExecutor.runAllReady(); - firstDownEvent.recycle(); - - // And another ACTION_DOWN is received without an ACTION_UP before - MotionEvent secondDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, secondDownEvent); - mBiometricExecutor.runAllReady(); - secondDownEvent.recycle(); - - // THEN the touch is still processed - verify(mFingerprintManager, times(2)).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onAodDownAndDownTouchReceived() throws RemoteException { - final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L, - 0L); - final TouchProcessorResult processorResultDown = - new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN, - -1 /* pointerId */, touchData); - - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); - - // WHEN fingerprint is requested because of AOD interrupt - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - mFgExecutor.runAllReady(); - - // and an ACTION_DOWN is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - processorResultDown); - MotionEvent firstDownEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, firstDownEvent); - mBiometricExecutor.runAllReady(); - firstDownEvent.recycle(); - - // THEN the touch is only processed once - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onTouch_pilferPointerWhenAltBouncerShowing() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - - // WHEN alternate bouncer is showing - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // WHEN ACTION_DOWN is received and touch is not within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent); - mBiometricExecutor.runAllReady(); - downEvent.recycle(); - - // THEN the touch is pilfered - verify(mInputManager).pilferPointers(any()); - } - - @Test - public void onTouch_doNotProcessTouchWhenPullingUpBouncer() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.UNCHANGED, InteractionEvent.UP, false); - - // GIVEN a swipe up to bring up primary bouncer is in progress or swipe down for QS - when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(true); - when(mLockscreenShadeTransitionController.getFractionToShade()).thenReturn(1f); - - // WHEN ACTION_MOVE is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN the touch is NOT processed - verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - - @Test - public void onTouch_qsDrag_processesTouchWhenAlternateBouncerVisible() - throws RemoteException { - final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult = - givenFingerEvent(InteractionEvent.DOWN, InteractionEvent.UP, false); - - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - // GIVEN swipe down for QS - when(mPrimaryBouncerInteractor.isInTransit()).thenReturn(false); - when(mLockscreenShadeTransitionController.getQSDragProgress()).thenReturn(1f); - - // WHEN ACTION_MOVE is received and touch is within sensor - when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn( - touchProcessorResult.first); - MotionEvent moveEvent = MotionEvent.obtain(0, 0, ACTION_MOVE, 0, 0, 0); - mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); - mBiometricExecutor.runAllReady(); - moveEvent.recycle(); - - // THEN the touch is still processed - verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), - anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), - anyBoolean()); - } - - @Test - public void onAodInterrupt_onAcquiredGood_fingerNoLongerDown() throws RemoteException { - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - - // THEN finger is considered down - assertTrue(mUdfpsController.isFingerDown()); - - // WHEN udfps receives an ACQUIRED_GOOD after the display is configured - when(mUdfpsView.isDisplayConfigured()).thenReturn(true); - verify(mFingerprintManager).setUdfpsOverlayController( - mUdfpsOverlayControllerCaptor.capture()); - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - - // THEN is fingerDown should be FALSE - assertFalse(mUdfpsController.isFingerDown()); - } - - @Test - public void playHaptic_onAodInterrupt_onAcquiredBad_performHapticFeedback() - throws RemoteException { - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - // GIVEN there's been an AoD interrupt - when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false); - mScreenObserver.onScreenTurnedOn(); - mUdfpsController.onAodInterrupt(0, 0, 0, 0); - - // THEN vibrate is used - verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS)); - } - - @Test - public void onAcquiredCalbacks() { - runWithAllParams( - this::ultrasonicCallbackOnAcquired); - } - - public void ultrasonicCallbackOnAcquired(TestParams testParams) throws RemoteException{ - if (testParams.sensorProps.sensorType - == FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC) { - reset(mUdfpsView); - - UdfpsController.Callback callbackMock = mock(UdfpsController.Callback.class); - mUdfpsController.addCallback(callbackMock); - - // GIVEN UDFPS overlay is showing - mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, - BiometricRequestConstants.REASON_AUTH_KEYGUARD, - mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - - verify(mFingerprintManager).setUdfpsOverlayController( - mUdfpsOverlayControllerCaptor.capture()); - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_START); - mFgExecutor.runAllReady(); - - verify(callbackMock).onFingerDown(); - - mUdfpsOverlayControllerCaptor.getValue().onAcquired(0, FINGERPRINT_ACQUIRED_GOOD); - mFgExecutor.runAllReady(); - - verify(callbackMock).onFingerUp(); - } - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java deleted file mode 100644 index 7986051de3e0..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.Flags; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityTransitionAnimator; -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionChangeEvent; -import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.shade.domain.interactor.ShadeInteractor; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.SystemUIDialogManager; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.user.domain.interactor.SelectedUserInteractor; -import com.android.systemui.util.concurrency.DelayableExecutor; - -import org.junit.Before; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { - // Dependencies - protected @Mock UdfpsKeyguardViewLegacy mView; - protected @Mock Context mResourceContext; - protected @Mock StatusBarStateController mStatusBarStateController; - protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager; - protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController; - protected @Mock DumpManager mDumpManager; - protected @Mock DelayableExecutor mExecutor; - protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - protected @Mock KeyguardStateController mKeyguardStateController; - protected @Mock KeyguardViewMediator mKeyguardViewMediator; - protected @Mock ConfigurationController mConfigurationController; - protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; - protected @Mock SystemUIDialogManager mDialogManager; - protected @Mock UdfpsController mUdfpsController; - protected @Mock ActivityTransitionAnimator mActivityTransitionAnimator; - protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; - protected @Mock ShadeInteractor mShadeInteractor; - protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor; - protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; - protected @Mock SelectedUserInteractor mSelectedUserInteractor; - protected @Mock KeyguardTransitionInteractor mKeyguardTransitionInteractor; - protected @Mock UdfpsOverlayInteractor mUdfpsOverlayInteractor; - - protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); - - protected UdfpsKeyguardViewControllerLegacy mController; - - // Capture listeners so that they can be used to send events - private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; - protected StatusBarStateController.StateListener mStatusBarStateListener; - - private @Captor ArgumentCaptor<KeyguardStateController.Callback> - mKeyguardStateControllerCallbackCaptor; - protected KeyguardStateController.Callback mKeyguardStateControllerCallback; - - private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.KeyguardViewManagerCallback> - mKeyguardViewManagerCallbackArgumentCaptor; - protected StatusBarKeyguardViewManager.KeyguardViewManagerCallback mKeyguardViewManagerCallback; - - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); - when(mView.getUnpausedAlpha()).thenReturn(255); - when(mShadeExpansionStateManager.addExpansionListener(any())).thenReturn( - new ShadeExpansionChangeEvent(0, false, false)); - mController = createUdfpsKeyguardViewController(); - } - - protected void sendStatusBarStateChanged(int statusBarState) { - mStatusBarStateListener.onStateChanged(statusBarState); - } - - protected void captureStatusBarStateListeners() { - verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture()); - mStatusBarStateListener = mStateListenerCaptor.getValue(); - } - - protected void captureKeyguardStateControllerCallback() { - verify(mKeyguardStateController).addCallback( - mKeyguardStateControllerCallbackCaptor.capture()); - mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); - } - - public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(false); - } - - public void captureKeyGuardViewManagerCallback() { - verify(mStatusBarKeyguardViewManager).addCallback( - mKeyguardViewManagerCallbackArgumentCaptor.capture()); - mKeyguardViewManagerCallback = mKeyguardViewManagerCallbackArgumentCaptor.getValue(); - } - - protected UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController( - boolean useModernBouncer) { - UdfpsKeyguardViewControllerLegacy controller = new UdfpsKeyguardViewControllerLegacy( - mView, - mStatusBarStateController, - mStatusBarKeyguardViewManager, - mKeyguardUpdateMonitor, - mDumpManager, - mLockscreenShadeTransitionController, - mConfigurationController, - mKeyguardStateController, - mUnlockedScreenOffAnimationController, - mDialogManager, - mUdfpsController, - mActivityTransitionAnimator, - mPrimaryBouncerInteractor, - mAlternateBouncerInteractor, - mUdfpsKeyguardAccessibilityDelegate, - mSelectedUserInteractor, - mKeyguardTransitionInteractor, - mShadeInteractor, - mUdfpsOverlayInteractor); - return controller; - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java deleted file mode 100644 index 98d8b054716c..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.testing.TestableLooper; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.systemui.statusbar.StatusBarState; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) - -@TestableLooper.RunWithLooper(setAsMainLooper = true) -public class UdfpsKeyguardViewLegacyControllerTest extends - UdfpsKeyguardViewLegacyControllerBaseTest { - @Override - public UdfpsKeyguardViewControllerLegacy createUdfpsKeyguardViewController() { - return createUdfpsKeyguardViewController(/* useModernBouncer */ false); - } - - @Test - public void testShouldPauseAuth_bouncerShowing() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); - when(mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()).thenReturn(true); - when(mView.getUnpausedAlpha()).thenReturn(0); - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testRegistersStatusBarStateListenersOnAttached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - } - - @Test - public void testViewControllerQueriesSBStateOnAttached() { - mController.onViewAttached(); - verify(mStatusBarStateController).getState(); - - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); - captureStatusBarStateListeners(); - - mController.onViewAttached(); - verify(mView, atLeast(1)).setPauseAuth(true); - } - - @Test - public void testListenersUnregisteredOnDetached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureKeyguardStateControllerCallback(); - mController.onViewDetached(); - - verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); - verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); - } - - @Test - public void testShouldPauseAuthUnpausedAlpha0() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - when(mView.getUnpausedAlpha()).thenReturn(0); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldNotPauseAuthOnKeyguard() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void onBiometricAuthenticated_pauseAuth() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN biometric is authenticated - captureKeyguardStateControllerCallback(); - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - mKeyguardStateControllerCallback.onUnlockedChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthIsLaunchTransitionFadingAway() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN isLaunchTransitionFadingAway=true - captureKeyguardStateControllerCallback(); - when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); - mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN not on keyguard yet (shade = home) - sendStatusBarStateChanged(StatusBarState.SHADE); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthAnimatingScreenOffFromShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN transitioning from home/shade => keyguard + animating screen off - mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testDoNotPauseAuthAnimatingScreenOffFromLS() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN animating screen off transition from LS => AOD - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN don't pause auth - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testOverrideShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - assertTrue(mController.shouldPauseAuth()); - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt deleted file mode 100644 index 29a6e56891af..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN -import com.android.systemui.biometrics.domain.interactor.udfpsOverlayInteractor -import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository -import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor -import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor -import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.TransitionState -import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -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.ArgumentMatchers.eq -import org.mockito.Mockito -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@RunWith(AndroidJUnit4::class) -@SmallTest -@TestableLooper.RunWithLooper -@kotlinx.coroutines.ExperimentalCoroutinesApi -class UdfpsKeyguardViewLegacyControllerWithCoroutinesTest : - UdfpsKeyguardViewLegacyControllerBaseTest() { - private val kosmos = testKosmos() - private val testScope = kosmos.testScope - - private val keyguardBouncerRepository = kosmos.fakeKeyguardBouncerRepository - private val transitionRepository = kosmos.fakeKeyguardTransitionRepository - - @Before - override fun setUp() { - allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread - MockitoAnnotations.initMocks(this) - super.setUp() - } - - override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewControllerLegacy { - mPrimaryBouncerInteractor = kosmos.primaryBouncerInteractor - mAlternateBouncerInteractor = kosmos.alternateBouncerInteractor - mKeyguardTransitionInteractor = kosmos.keyguardTransitionInteractor - mUdfpsOverlayInteractor = kosmos.udfpsOverlayInteractor - return createUdfpsKeyguardViewController(/* useModernBouncer */ true) - } - - @Test - fun bouncerExpansionChange_fadeIn() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - captureKeyguardStateControllerCallback() - Mockito.reset(mView) - - // WHEN status bar expansion is 0 - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN alpha is 0 - verify(mView).unpausedAlpha = 0 - - job.cancel() - } - - @Test - fun bouncerExpansionChange_pauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - Mockito.reset(mView) - - // WHEN panelViewExpansion changes to hide - whenever(mView.unpausedAlpha).thenReturn(0) - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN pause auth is updated to PAUSE - verify(mView, Mockito.atLeastOnce()).setPauseAuth(true) - - job.cancel() - } - - @Test - fun bouncerExpansionChange_unpauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the keyguard + panel expansion is 0f - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - Mockito.reset(mView) - - // WHEN panelViewExpansion changes to expanded - whenever(mView.unpausedAlpha).thenReturn(255) - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - - // THEN pause auth is updated to NOT pause - verify(mView, Mockito.atLeastOnce()).setPauseAuth(false) - - job.cancel() - } - - @Test - fun shadeLocked_showAlternateBouncer_unpauseAuth() = - testScope.runTest { - // GIVEN view is attached + on the SHADE_LOCKED (udfps view not showing) - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED) - - // WHEN alternate bouncer is requested - val job = mController.listenForAlternateBouncerVisibility(this) - keyguardBouncerRepository.setAlternateVisible(true) - runCurrent() - - // THEN udfps view will animate in & pause auth is updated to NOT pause - verify(mView).animateInUdfpsBouncer(any()) - assertFalse(mController.shouldPauseAuth()) - - job.cancel() - } - - /** After migration to MODERN_BOUNCER, replaces UdfpsKeyguardViewControllerTest version */ - @Test - fun shouldPauseAuthBouncerShowing() = - testScope.runTest { - // GIVEN view attached and we're on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - - // WHEN the bouncer expansion is VISIBLE - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN UDFPS shouldPauseAuth == true - assertTrue(mController.shouldPauseAuth()) - - job.cancel() - } - - @Test - fun shouldHandleTouchesChange() = - testScope.runTest { - val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches) - - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - whenever(mView.setPauseAuth(true)).thenReturn(true) - whenever(mView.unpausedAlpha).thenReturn(0) - - // WHEN panelViewExpansion changes to expanded - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - // THEN UDFPS auth is paused and should not handle touches - assertThat(mController.shouldPauseAuth()).isTrue() - assertThat(shouldHandleTouches!!).isFalse() - - job.cancel() - } - - @Test - fun shouldHandleTouchesOnDetach() = - testScope.runTest { - val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches) - - // GIVEN view is attached + on the keyguard - mController.onViewAttached() - captureStatusBarStateListeners() - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - whenever(mView.setPauseAuth(true)).thenReturn(true) - whenever(mView.unpausedAlpha).thenReturn(0) - - // WHEN panelViewExpansion changes to expanded - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) - runCurrent() - - mController.onViewDetached() - - // THEN UDFPS auth is paused and should not handle touches - assertThat(mController.shouldPauseAuth()).isTrue() - assertThat(shouldHandleTouches!!).isFalse() - - job.cancel() - } - - @Test - fun fadeFromDialogSuggestedAlpha() = - testScope.runTest { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached() - captureStatusBarStateListeners() - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - Mockito.reset(mView) - - // WHEN dialog suggested alpha is .6f - whenever(mView.dialogSuggestedAlpha).thenReturn(.6f) - sendStatusBarStateChanged(StatusBarState.KEYGUARD) - - // THEN alpha is updated based on dialog suggested alpha - verify(mView).unpausedAlpha = (.6f * 255).toInt() - - job.cancel() - } - - @Test - fun transitionToFullShadeProgress() = - testScope.runTest { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached() - val job = mController.listenForBouncerExpansion(this) - keyguardBouncerRepository.setPrimaryShow(true) - keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_HIDDEN) - runCurrent() - Mockito.reset(mView) - whenever(mView.dialogSuggestedAlpha).thenReturn(1f) - - // WHEN we're transitioning to the full shade - val transitionProgress = .6f - mController.setTransitionToFullShadeProgress(transitionProgress) - - // THEN alpha is between 0 and 255 - verify(mView).unpausedAlpha = ((1f - transitionProgress) * 255).toInt() - - job.cancel() - } - - @Test - fun aodToLockscreen_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForLockscreenAodTransitions(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - job.cancel() - } - - @Test - fun lockscreenToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForLockscreenAodTransitions(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN) - ) - - job.cancel() - } - - @Test - fun goneToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForGoneToAodTransition(this) - - // WHEN transitioning from lockscreen to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - job.cancel() - } - - @Test - fun aodToOccluded_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForAodToOccludedTransitions(this) - - // WHEN transitioning from aod to occluded - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged(eq(.7f), eq(.7f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.OCCLUDED, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(UdfpsKeyguardViewLegacy.ANIMATION_NONE)) - - job.cancel() - } - - @Test - fun occludedToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForOccludedToAodTransition(this) - - // WHEN transitioning from occluded to aod - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(.3f), - eq(.3f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.OCCLUDED, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.FINISHED - ) - ) - runCurrent() - // THEN doze amount is updated - verify(mView) - .onDozeAmountChanged( - eq(1f), - eq(1f), - eq(UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF) - ) - - job.cancel() - } - - @Test - fun cancelledAodToLockscreen_dozeAmountChangedToZero() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - val job = mController.listenForLockscreenAodTransitions(this) - runCurrent() - Mockito.reset(mView) - - // WHEN aod to lockscreen transition is cancelled - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.AOD, - to = KeyguardState.LOCKSCREEN, - value = 1f, - transitionState = TransitionState.CANCELED - ) - ) - runCurrent() - // ... and WHEN the next transition is from lockscreen => occluded - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.OCCLUDED, - value = .4f, - transitionState = TransitionState.STARTED - ) - ) - runCurrent() - - // THEN doze amount is updated to zero - verify(mView) - .onDozeAmountChanged(eq(0f), eq(0f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) - job.cancel() - } - - @Test - fun cancelledLockscreenToAod_dozeAmountNotUpdatedToZero() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - val job = mController.listenForLockscreenAodTransitions(this) - runCurrent() - Mockito.reset(mView) - - // WHEN lockscreen to aod transition is cancelled - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - value = 1f, - transitionState = TransitionState.CANCELED - ) - ) - runCurrent() - - // THEN doze amount is NOT updated to zero - verify(mView, never()).onDozeAmountChanged(eq(0f), eq(0f), anyInt()) - job.cancel() - } - - @Test - fun dreamingToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForDreamingToAodTransitions(this) - // WHEN dreaming to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.DREAMING, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) - job.cancel() - } - - @Test - fun alternateBouncerToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForAlternateBouncerToAodTransitions(this) - // WHEN alternate bouncer to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.ALTERNATE_BOUNCER, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView) - .onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN)) - job.cancel() - } - - @Test - fun bouncerToAod_dozeAmountChanged() = - testScope.runTest { - // GIVEN view is attached - mController.onViewAttached() - Mockito.reset(mView) - - val job = mController.listenForPrimaryBouncerToAodTransitions(this) - // WHEN alternate bouncer to aod transition in progress - transitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.PRIMARY_BOUNCER, - to = KeyguardState.AOD, - value = .3f, - transitionState = TransitionState.RUNNING - ) - ) - runCurrent() - - // THEN doze amount is updated to - verify(mView).onDozeAmountChanged(eq(.3f), eq(.3f), eq(ANIMATE_APPEAR_ON_SCREEN_OFF)) - job.cancel() - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index 68cfa28dabd7..82ff61795e98 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -16,12 +16,9 @@ package com.android.systemui.bouncer.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.keyguard.keyguardUpdateMonitor -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.authenticationInteractor @@ -61,12 +58,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { underTest = kosmos.alternateBouncerInteractor } - @Test(expected = IllegalStateException::class) - @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun enableUdfpsRefactor_deprecatedShowMethod_throwsIllegalStateException() { - underTest.show() - } - @Test @DisableSceneContainer fun canShowAlternateBouncer_false_dueToTransitionState() = @@ -101,21 +92,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun canShowAlternateBouncerForFingerprint_givenCanShow() { - givenCanShowAlternateBouncer() - assertTrue(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test - fun canShowAlternateBouncerForFingerprint_alternateBouncerUIUnavailable() { - givenCanShowAlternateBouncer() - kosmos.keyguardBouncerRepository.setAlternateBouncerUIAvailable(false) - - assertFalse(underTest.canShowAlternateBouncerForFingerprint()) - } - - @Test fun canShowAlternateBouncerForFingerprint_ifFingerprintIsNotUsuallyAllowed() { givenCanShowAlternateBouncer() kosmos.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) @@ -140,15 +116,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun show_whenCanShow() { - givenCanShowAlternateBouncer() - - assertTrue(underTest.show()) - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value) - } - - @Test fun canShowAlternateBouncerForFingerprint_butCanDismissLockScreen() { givenCanShowAlternateBouncer() whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(true) @@ -165,15 +132,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun show_whenCannotShow() { - givenCannotShowAlternateBouncer() - - assertFalse(underTest.show()) - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerVisible.value) - } - - @Test fun hide_wasPreviouslyShowing() { kosmos.keyguardBouncerRepository.setAlternateVisible(true) @@ -190,7 +148,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { } @Test - @EnableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun canShowAlternateBouncerForFingerprint_rearFps() { givenCanShowAlternateBouncer() kosmos.fingerprintPropertyRepository.supportsRearFps() // does not support alternate bouncer @@ -198,29 +155,6 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { assertFalse(underTest.canShowAlternateBouncerForFingerprint()) } - @Test - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - fun alternateBouncerUiAvailable_fromMultipleSources() { - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // GIVEN there are two different sources indicating the alternate bouncer is available - underTest.setAlternateBouncerUIAvailable(true, "source1") - underTest.setAlternateBouncerUIAvailable(true, "source2") - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // WHEN one of the sources no longer says the UI is available - underTest.setAlternateBouncerUIAvailable(false, "source1") - - // THEN alternate bouncer UI is still available (from the other source) - assertTrue(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - - // WHEN all sources say the UI is not available - underTest.setAlternateBouncerUIAvailable(false, "source2") - - // THEN alternate boucer UI is not available - assertFalse(kosmos.keyguardBouncerRepository.alternateBouncerUIAvailable.value) - } - private fun givenAlternateBouncerSupported() { kosmos.givenAlternateBouncerSupported() } @@ -228,8 +162,4 @@ class AlternateBouncerInteractorTest : SysuiTestCase() { private fun givenCanShowAlternateBouncer() { kosmos.givenCanShowAlternateBouncer() } - - private fun givenCannotShowAlternateBouncer() { - kosmos.givenCannotShowAlternateBouncer() - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt index 8402676dbd6b..18f33e4e1cd6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R @@ -54,6 +55,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { screenBrightnessInteractor, brightnessPolicyEnforcementInteractor, applicationCoroutineScope, + sliderHapticsViewModelFactory, ) } @@ -61,7 +63,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { fun setUp() { kosmos.fakeScreenBrightnessRepository.setMinMaxBrightness( LinearBrightness(minBrightness), - LinearBrightness(maxBrightness) + LinearBrightness(maxBrightness), ) } @@ -79,7 +81,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { BrightnessUtils.convertLinearToGammaFloat( brightness, minBrightness, - maxBrightness + maxBrightness, ) ) @@ -91,7 +93,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { BrightnessUtils.convertLinearToGammaFloat( brightness, minBrightness, - maxBrightness + maxBrightness, ) ) } @@ -122,7 +124,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { BrightnessUtils.convertGammaToLinearFloat( newBrightness, minBrightness, - maxBrightness + maxBrightness, ) val drag = Drag.Dragging(GammaBrightness(newBrightness)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index ee65fbd810ae..1e8651683ec1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal +import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings @@ -77,7 +78,11 @@ class CommunalSceneStartableTest : SysuiTestCase() { @Before fun setUp() { with(kosmos) { - fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT) + fakeSettings.putIntForUser( + Settings.System.SCREEN_OFF_TIMEOUT, + SCREEN_TIMEOUT, + UserHandle.USER_CURRENT, + ) kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true) underTest = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt index 6c6de61c638a..cd8b2e12a3d5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt @@ -440,6 +440,28 @@ class DisplayRepositoryTest : SysuiTestCase() { } @Test + fun displayAdditionEvent_emptyByDefault() = + testScope.runTest { + setDisplays(1, 2, 3) + + val lastAddedDisplay by lastDisplayAdditionEvent() + + assertThat(lastAddedDisplay).isNull() + } + + @Test + fun displayAdditionEvent_displaysAdded_doesNotReplayEventsToNewSubscribers() = + testScope.runTest { + val priorDisplayAdded by lastDisplayAdditionEvent() + setDisplays(1) + sendOnDisplayAdded(1) + assertThat(priorDisplayAdded?.displayId).isEqualTo(1) + + val lastAddedDisplay by collectLastValue(displayRepository.displayAdditionEvent) + assertThat(lastAddedDisplay).isNull() + } + + @Test fun defaultDisplayOff_changes() = testScope.runTest { val defaultDisplayOff by latestDefaultDisplayOffFlowValue() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt index 4a80d7242e03..28f88fe1d84e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProviderTest.kt @@ -17,13 +17,11 @@ package com.android.systemui.haptics.slider import android.os.VibrationEffect -import android.view.VelocityTracker import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.haptics.fakeVibratorHelper import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.fakeSystemClock import kotlin.math.max import kotlin.test.assertEquals @@ -31,19 +29,17 @@ import kotlin.test.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) class SliderHapticFeedbackProviderTest : SysuiTestCase() { - @Mock private lateinit var velocityTracker: VelocityTracker - private val kosmos = testKosmos() private val config = SliderHapticFeedbackConfig() + private val dragVelocityProvider = SliderDragVelocityProvider { config.maxVelocityToScale } + private val lowTickDuration = 12 // Mocked duration of a low tick private val dragTextureThresholdMillis = lowTickDuration * config.numberOfLowTicks + config.deltaMillisForDragInterval @@ -52,17 +48,13 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { @Before fun setup() { - MockitoAnnotations.initMocks(this) - whenever(velocityTracker.isAxisSupported(config.velocityAxis)).thenReturn(true) - whenever(velocityTracker.getAxisVelocity(config.velocityAxis)) - .thenReturn(config.maxVelocityToScale) vibratorHelper.primitiveDurations[VibrationEffect.Composition.PRIMITIVE_LOW_TICK] = lowTickDuration sliderHapticFeedbackProvider = SliderHapticFeedbackProvider( vibratorHelper, - velocityTracker, + dragVelocityProvider, config, kosmos.fakeSystemClock, ) @@ -75,9 +67,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision( - config.maxVelocityToScale - ), + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) .compose() @@ -93,7 +83,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale) + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) .compose() @@ -110,9 +100,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision( - config.maxVelocityToScale - ), + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) .compose() @@ -128,9 +116,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision( - config.maxVelocityToScale - ), + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) .compose() @@ -146,10 +132,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { // GIVEN max velocity and slider progress val progress = 1f val expectedScale = - sliderHapticFeedbackProvider.scaleOnDragTexture( - config.maxVelocityToScale, - progress, - ) + sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress) val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) @@ -222,10 +205,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { // GIVEN max velocity and slider progress val progress = 1f val expectedScale = - sliderHapticFeedbackProvider.scaleOnDragTexture( - config.maxVelocityToScale, - progress, - ) + sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress) val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) @@ -234,9 +214,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision( - config.maxVelocityToScale - ), + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) .compose() @@ -250,7 +228,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { // THEN there are two bookend vibrations assertEquals( /* expected= */ 2, - vibratorHelper.timesVibratedWithEffect(bookendVibration) + vibratorHelper.timesVibratedWithEffect(bookendVibration), ) } @@ -260,10 +238,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { // GIVEN max velocity and slider progress val progress = 1f val expectedScale = - sliderHapticFeedbackProvider.scaleOnDragTexture( - config.maxVelocityToScale, - progress, - ) + sliderHapticFeedbackProvider.scaleOnDragTexture(config.maxVelocityToScale, progress) val ticks = VibrationEffect.startComposition() repeat(config.numberOfLowTicks) { ticks.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK, expectedScale) @@ -272,9 +247,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { VibrationEffect.startComposition() .addPrimitive( VibrationEffect.Composition.PRIMITIVE_CLICK, - sliderHapticFeedbackProvider.scaleOnEdgeCollision( - config.maxVelocityToScale - ), + sliderHapticFeedbackProvider.scaleOnEdgeCollision(config.maxVelocityToScale), ) .compose() @@ -288,7 +261,7 @@ class SliderHapticFeedbackProviderTest : SysuiTestCase() { // THEN there are two bookend vibrations assertEquals( /* expected= */ 2, - vibratorHelper.timesVibratedWithEffect(bookendVibration) + vibratorHelper.timesVibratedWithEffect(bookendVibration), ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModelTest.kt new file mode 100644 index 000000000000..8693f6b1d927 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModelTest.kt @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics.slider.compose.ui + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.interaction.DragInteraction +import androidx.compose.foundation.interaction.InteractionSource +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig +import com.android.systemui.haptics.slider.SliderEventType +import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig +import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory +import com.android.systemui.kosmos.testScope +import com.android.systemui.lifecycle.activateIn +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class SliderHapticsViewModelTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val interactionSource = DragInteractionSourceTest() + private val underTest = + kosmos.sliderHapticsViewModelFactory.create( + interactionSource, + 0f..1f, + Orientation.Horizontal, + SliderHapticFeedbackConfig(), + SeekableSliderTrackerConfig(), + ) + + @Before + fun setUp() { + underTest.activateIn(testScope) + } + + @Test + fun onActivated_startsRunning() = + testScope.runTest { + // WHEN the view-model is activated + testScope.runCurrent() + + // THEN the view-model starts running + assertThat(underTest.isRunning).isTrue() + } + + @Test + fun onDragStart_goesToUserStartedDragging() = + testScope.runTest { + // WHEN a drag interaction starts + interactionSource.setDragInteraction(DragInteraction.Start()) + runCurrent() + + // THEN the current slider event type shows that the user started dragging + assertThat(underTest.currentSliderEventType) + .isEqualTo(SliderEventType.STARTED_TRACKING_TOUCH) + } + + @Test + fun onValueChange_whileUserStartedDragging_goesToUserDragging() = + testScope.runTest { + // WHEN a drag interaction starts + interactionSource.setDragInteraction(DragInteraction.Start()) + runCurrent() + + // WHEN a value changes in the slider + underTest.onValueChange(0.5f) + + // THEN the current slider event type shows that the user is dragging + assertThat(underTest.currentSliderEventType) + .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_USER) + } + + @Test + fun onValueChange_whileUserDragging_staysInUserDragging() = + testScope.runTest { + // WHEN a drag interaction starts and the user keeps dragging + interactionSource.setDragInteraction(DragInteraction.Start()) + runCurrent() + underTest.onValueChange(0.5f) + + // WHEN value changes continue to occur due to dragging + underTest.onValueChange(0.6f) + + // THEN the current slider event type reflects that the user continues to drag + assertThat(underTest.currentSliderEventType) + .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_USER) + } + + @Test + fun onValueChange_whileNOTHING_goesToProgramStartedDragging() = + testScope.runTest { + // WHEN a value change occurs without a drag interaction + underTest.onValueChange(0.5f) + + // THEN the current slider event type shows that the program started dragging + assertThat(underTest.currentSliderEventType) + .isEqualTo(SliderEventType.STARTED_TRACKING_PROGRAM) + } + + @Test + fun onValueChange_whileProgramStartedDragging_goesToProgramDragging() = + testScope.runTest { + // WHEN the program starts dragging + underTest.onValueChange(0.5f) + + // WHEN the program continues to make value changes + underTest.onValueChange(0.6f) + + // THEN the current slider event type shows that program is dragging + assertThat(underTest.currentSliderEventType) + .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) + } + + @Test + fun onValueChange_whileProgramDragging_staysInProgramDragging() = + testScope.runTest { + // WHEN the program starts and continues to drag + underTest.onValueChange(0.5f) + underTest.onValueChange(0.6f) + + // WHEN value changes continue to occur + underTest.onValueChange(0.7f) + + // THEN the current slider event type shows that the program is dragging the slider + assertThat(underTest.currentSliderEventType) + .isEqualTo(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM) + } + + @Test + fun onValueChangeEnded_goesToNOTHING() = + testScope.runTest { + // WHEN changes end in the slider + underTest.onValueChangeEnded() + + // THEN the current slider event type always resets to NOTHING + assertThat(underTest.currentSliderEventType).isEqualTo(SliderEventType.NOTHING) + } + + private class DragInteractionSourceTest : InteractionSource { + private val _interactions = MutableStateFlow<DragInteraction>(IdleDrag) + override val interactions = _interactions.asStateFlow() + + fun setDragInteraction(interaction: DragInteraction) { + _interactions.value = interaction + } + } + + private object IdleDrag : DragInteraction +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt index 2a2a82d53671..b5ea305544ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt @@ -39,7 +39,6 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.kosmos.testScope -import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes @@ -100,18 +99,14 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { FakeTrustRepository(), testScope.backgroundScope, mSelectedUserInteractor, - faceAuthInteractor + faceAuthInteractor, ) alternateBouncerInteractor = AlternateBouncerInteractor( - mock(StatusBarStateController::class.java), - mock(KeyguardStateController::class.java), bouncerRepository, FakeFingerprintPropertyRepository(), - biometricSettingsRepository, FakeSystemClock(), - keyguardUpdateMonitor, { mock(DeviceEntryBiometricsAllowedInteractor::class.java) }, { mock(KeyguardInteractor::class.java) }, { mock(KeyguardTransitionInteractor::class.java) }, @@ -121,13 +116,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) } @@ -142,7 +136,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isTrue() } @@ -158,7 +152,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = false, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -169,13 +163,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { testScope.runTest { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) val showIndicatorForDeviceEntry by @@ -185,7 +178,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isTrue() } @@ -196,13 +189,12 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { testScope.runTest { underTest = DeviceEntrySideFpsOverlayInteractor( - testScope.backgroundScope, mContext, deviceEntryFingerprintAuthRepository, kosmos.sceneInteractor, primaryBouncerInteractor, alternateBouncerInteractor, - keyguardUpdateMonitor + keyguardUpdateMonitor, ) val showIndicatorForDeviceEntry by @@ -212,7 +204,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -228,7 +220,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = false, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -245,7 +237,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = false + isUnlockingWithFpAllowed = false, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -261,7 +253,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = false, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -277,7 +269,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { updateBouncerScene( isActive = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = false + isUnlockingWithFpAllowed = false, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -294,7 +286,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = true, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) assertThat(showIndicatorForDeviceEntry).isFalse() } @@ -325,7 +317,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { isShowing = true, isAnimatingAway = false, fpsDetectionRunning = true, - isUnlockingWithFpAllowed = true + isUnlockingWithFpAllowed = true, ) // Another request to show indicator for deviceEntryFingerprintAuthRepository update @@ -355,7 +347,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, - true + true, ) } @@ -366,7 +358,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { ) { kosmos.sceneInteractor.changeScene( if (isActive) Scenes.Bouncer else Scenes.Lockscreen, - "reason" + "reason", ) whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning) @@ -375,7 +367,7 @@ class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() { .thenReturn(isUnlockingWithFpAllowed) mContext.orCreateTestableResources.addOverride( R.bool.config_show_sidefps_hint_on_bouncer, - true + true, ) runCurrent() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt index c4eabd84e031..380060865282 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.keyguard.ui.binder -import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import android.view.View import android.view.layoutInflater @@ -24,7 +23,6 @@ import android.view.mockedLayoutInflater import android.view.windowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.givenCanShowAlternateBouncer import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -63,7 +61,7 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { kosmos.mockedLayoutInflater.inflate( eq(R.layout.alternate_bouncer), isNull(), - anyBoolean() + anyBoolean(), ) ) .thenReturn(mockedAltBouncerView) @@ -71,7 +69,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun addViewToWindowManager() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() @@ -85,7 +82,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun viewRemovedImmediatelyIfAlreadyAttachedToWindow() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() @@ -107,7 +103,6 @@ class AlternateBouncerViewBinderTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) fun viewNotRemovedUntilAttachedToWindow() { testScope.runTest { kosmos.givenCanShowAlternateBouncer() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt index 1c99eff0d328..d94c97af6f14 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt @@ -28,12 +28,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel -import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.util.mockito.any @@ -135,7 +135,7 @@ class SmartspaceSectionTest : SysuiTestCase() { assertThat(smartspaceConstraints.layout.topToBottom).isEqualTo(dateView.id) val dateConstraints = constraintSet.getConstraint(dateView.id) - assertThat(dateConstraints.layout.topToBottom).isEqualTo(R.id.lockscreen_clock_view) + assertThat(dateConstraints.layout.topToBottom).isEqualTo(customR.id.lockscreen_clock_view) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt index c1bd37811787..5d9548057bae 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt @@ -19,7 +19,6 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository import com.android.systemui.coroutines.collectLastValue @@ -34,7 +33,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 @ExperimentalCoroutinesApi @RunWith(AndroidJUnit4::class) @@ -49,7 +47,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsUdfps() @@ -64,28 +61,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { assertThat(alternateBouncerWindowRequired).isTrue() transitionRepository.sendTransitionSteps( - listOf( - stepFromAlternateBouncer(1.0f, TransitionState.FINISHED), - ), - testScope, - ) - assertThat(alternateBouncerWindowRequired).isFalse() - } - - @Test - fun deviceEntryUdfpsFlagDisabled_alternateBouncerWindowRequiredFalse() = - testScope.runTest { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - val alternateBouncerWindowRequired by - collectLastValue(underTest.alternateBouncerWindowRequired) - fingerprintPropertyRepository.supportsUdfps() - transitionRepository.sendTransitionSteps( - listOf( - stepFromAlternateBouncer(0f, TransitionState.STARTED), - stepFromAlternateBouncer(.4f), - stepFromAlternateBouncer(.6f), - stepFromAlternateBouncer(1f), - ), + listOf(stepFromAlternateBouncer(1.0f, TransitionState.FINISHED)), testScope, ) assertThat(alternateBouncerWindowRequired).isFalse() @@ -94,7 +70,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun lockscreenTransition_alternateBouncerWindowRequiredFalse() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsUdfps() @@ -113,7 +88,6 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { @Test fun rearFps_alternateBouncerWindowRequiredFalse() = testScope.runTest { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val alternateBouncerWindowRequired by collectLastValue(underTest.alternateBouncerWindowRequired) fingerprintPropertyRepository.supportsRearFps() @@ -131,7 +105,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { private fun stepFromAlternateBouncer( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.ALTERNATE_BOUNCER, @@ -143,7 +117,7 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { private fun stepFromDozingToLockscreen( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.DOZING, @@ -157,14 +131,14 @@ class AlternateBouncerWindowViewModelTest : SysuiTestCase() { from: KeyguardState, to: KeyguardState, value: Float, - transitionState: TransitionState + transitionState: TransitionState, ): TransitionStep { return TransitionStep( from = from, to = to, value = value, transitionState = transitionState, - ownerName = "AlternateBouncerViewModelTest" + ownerName = "AlternateBouncerViewModelTest", ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt index 14d60943149f..e5bdc2e56b11 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepositoryTest.kt @@ -54,7 +54,7 @@ class PaginatedGridRepositoryTest : SysuiTestCase() { private fun setRowsInConfig(rows: Int) = with(kosmos) { testCase.context.orCreateTestableResources.addOverride( - R.integer.quick_settings_max_rows, + R.integer.quick_settings_paginated_grid_num_rows, rows, ) fakeConfigurationRepository.onConfigurationChange() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt index ae6f576bcf3e..cda3d488cb1e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepositoryTest.kt @@ -54,7 +54,7 @@ class QuickQuickSettingsRowRepositoryTest : SysuiTestCase() { private fun setRowsInConfig(rows: Int) = with(kosmos) { testCase.context.orCreateTestableResources.addOverride( - R.integer.quick_qs_panel_max_rows, + R.integer.quick_qs_paginated_grid_num_rows, rows, ) fakeConfigurationRepository.onConfigurationChange() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt index a1c0ef2789d5..2c894f9aa20f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModelTest.kt @@ -151,7 +151,7 @@ class QuickQuickSettingsViewModelTest : SysuiTestCase() { private fun Kosmos.setRows(rows: Int) { testCase.context.orCreateTestableResources.addOverride( - R.integer.quick_qs_panel_max_rows, + R.integer.quick_qs_paginated_grid_num_rows, rows, ) fakeConfigurationRepository.onConfigurationChange() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 3f550ca27868..0d5ddaeedb9e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -18,9 +18,12 @@ package com.android.systemui.screenrecord; import static com.android.systemui.screenrecord.RecordingService.GROUP_KEY_ERROR_SAVING; import static com.android.systemui.screenrecord.RecordingService.GROUP_KEY_SAVED; +import static com.android.systemui.screenrecord.RecordingService.NOTIF_GROUP_ID_ERROR_SAVING; +import static com.android.systemui.screenrecord.RecordingService.NOTIF_GROUP_ID_SAVED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -235,7 +238,9 @@ public class RecordingServiceTest extends SysuiTestCase { // Processing notification ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class); - verify(mNotificationManager).notifyAsUser(any(), anyInt(), notifCaptor.capture(), any()); + ArgumentCaptor<Integer> notifIdCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mNotificationManager) + .notifyAsUser(any(), notifIdCaptor.capture(), notifCaptor.capture(), any()); assertEquals(GROUP_KEY_SAVED, notifCaptor.getValue().getGroup()); reset(mNotificationManager); @@ -243,7 +248,7 @@ public class RecordingServiceTest extends SysuiTestCase { mRunnableCaptor.getValue().run(); verify(mNotificationManager, times(2)) - .notifyAsUser(any(), anyInt(), notifCaptor.capture(), any()); + .notifyAsUser(any(), notifIdCaptor.capture(), notifCaptor.capture(), any()); // Saved notification Notification saveNotification = notifCaptor.getAllValues().get(0); assertFalse(saveNotification.isGroupSummary()); @@ -252,6 +257,10 @@ public class RecordingServiceTest extends SysuiTestCase { Notification groupSummaryNotification = notifCaptor.getAllValues().get(1); assertTrue(groupSummaryNotification.isGroupSummary()); assertEquals(GROUP_KEY_SAVED, groupSummaryNotification.getGroup()); + + // Verify the group notification ID and the individual notification ID are different + assertNotEquals(NOTIF_GROUP_ID_SAVED, (int) notifIdCaptor.getAllValues().get(0)); + assertEquals(NOTIF_GROUP_ID_SAVED, (int) notifIdCaptor.getAllValues().get(1)); } @Test @@ -264,9 +273,12 @@ public class RecordingServiceTest extends SysuiTestCase { verify(mRecordingService).createErrorSavingNotification(any()); ArgumentCaptor<Notification> notifCaptor = ArgumentCaptor.forClass(Notification.class); - verify(mNotificationManager).notifyAsUser(any(), anyInt(), notifCaptor.capture(), any()); + ArgumentCaptor<Integer> notifIdCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mNotificationManager) + .notifyAsUser(any(), notifIdCaptor.capture(), notifCaptor.capture(), any()); assertTrue(notifCaptor.getValue().isGroupSummary()); assertEquals(GROUP_KEY_ERROR_SAVING, notifCaptor.getValue().getGroup()); + assertEquals(NOTIF_GROUP_ID_ERROR_SAVING, (int) notifIdCaptor.getValue()); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index 0454317b5f04..06d19d7f9822 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -68,13 +68,13 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; +import com.android.keyguard.EmptyLockIconViewController; import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardSliceViewController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.LegacyLockIconViewController; import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; import com.android.keyguard.dagger.KeyguardStatusViewComponent; @@ -271,6 +271,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController; @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent; @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; + @Mock protected EmptyLockIconViewController mLockIconViewController; @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController; @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController; @@ -285,7 +286,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected AmbientState mAmbientState; @Mock protected UserManager mUserManager; @Mock protected UiEventLogger mUiEventLogger; - @Mock protected LegacyLockIconViewController mLockIconViewController; @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator; @Mock protected KeyguardRootView mKeyguardRootView; @Mock protected View mKeyguardRootViewChild; @@ -397,7 +397,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR); - mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); mMainDispatcher = getMainDispatcher(); KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps = @@ -687,6 +686,9 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(longPressHandlingView.getResources()).thenReturn(longPressHandlingViewRes); when(longPressHandlingViewRes.getString(anyInt())).thenReturn(""); + when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild); + when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView); + mNotificationPanelViewController = new NotificationPanelViewController( mView, mMainHandler, @@ -852,7 +854,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel(); mNotificationPanelViewController.cancelHeightAnimator(); leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream() - .filter(Animator::isRunning).toList(); + .filter(Animator::isRunning).toList(); mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel); } if (mMainHandler != null) { @@ -869,11 +871,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom); when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom); - when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding)); - when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding)); - when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild); - when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView); when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding)) .thenReturn(indicationPadding); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 43dbb40d7721..ec75972aecfe 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -217,7 +217,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(5); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(5); } @@ -235,7 +234,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); } @@ -253,7 +251,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); } @@ -271,7 +268,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(2); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(2); } @@ -289,7 +285,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf()) .isEqualTo(0); } @@ -389,7 +384,6 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo @Test @DisableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) public void alternateBouncerVisible_onTouchEvent_notHandled() { - mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); // GIVEN alternate bouncer is visible when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 6f2302a22d7b..9fe52991c3a0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -30,7 +30,6 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityContainerController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.Flags import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT @@ -54,7 +53,6 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.res.R -import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor import com.android.systemui.statusbar.DragDownHelper @@ -71,7 +69,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider @@ -80,6 +77,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow @@ -98,11 +96,10 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters -import java.util.Optional -import org.mockito.Mockito.`when` as whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -125,12 +122,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController - @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var quickSettingsController: QuickSettingsControllerImpl @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController @Mock private lateinit var pulsingGestureListener: PulsingGestureListener @Mock @@ -144,7 +139,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock private lateinit var unfoldTransitionProgressProvider: - Optional<UnfoldTransitionProgressProvider> + Optional<UnfoldTransitionProgressProvider> @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var dragDownHelper: DragDownHelper @Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor @@ -176,20 +171,17 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : MockitoAnnotations.initMocks(this) whenever(view.bottom).thenReturn(VIEW_BOTTOM) whenever(view.findViewById<ViewGroup>(R.id.keyguard_bouncer_container)) - .thenReturn(mock(ViewGroup::class.java)) + .thenReturn(mock(ViewGroup::class.java)) whenever(keyguardBouncerComponentFactory.create(any(ViewGroup::class.java))) - .thenReturn(keyguardBouncerComponent) + .thenReturn(keyguardBouncerComponent) whenever(keyguardBouncerComponent.securityContainerController) - .thenReturn(keyguardSecurityContainerController) + .thenReturn(keyguardSecurityContainerController) whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING))) - .thenReturn(emptyFlow<TransitionStep>()) + .thenReturn(emptyFlow<TransitionStep>()) featureFlagsClassic = FakeFeatureFlagsClassic() featureFlagsClassic.set(SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlagsClassic.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - if (!SceneContainerFlag.isEnabled) { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES) testScope = TestScope() @@ -208,9 +200,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : panelExpansionInteractor, ShadeExpansionStateManager(), stackScrollLayoutController, - statusBarKeyguardViewManager, statusBarWindowStateController, - lockIconViewController, centralSurfaces, dozeServiceHost, dozeScrimController, @@ -233,7 +223,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : quickSettingsController, primaryBouncerInteractor, alternateBouncerInteractor, - mock(BouncerViewBinder::class.java) + mock(BouncerViewBinder::class.java), ) underTest.setupExpandedStatusBar() underTest.setDragDownHelper(dragDownHelper) @@ -294,7 +284,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -309,7 +299,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Item we're testing whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(false) @@ -327,7 +317,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) // Item we're testing whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(false) + .thenReturn(false) val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -341,7 +331,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Item we're testing whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false) @@ -358,7 +348,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) whenever(panelExpansionInteractor.isFullyCollapsed).thenReturn(true) whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) + .thenReturn(true) // Down event first interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT) @@ -379,7 +369,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : // GIVEN touch dispatcher in a state that returns true underTest.setStatusBarViewController(phoneStatusBarViewController) whenever(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) - .thenReturn(true) + .thenReturn(true) assertThat(interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)).isTrue() // WHEN launch animation is running for 2 seconds @@ -432,47 +422,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : } @Test - fun shouldInterceptTouchEvent_statusBarKeyguardViewManagerShouldIntercept() { - // down event should be intercepted by keyguardViewManager - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(true) - - // Then touch should not be intercepted - val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT) - assertThat(shouldIntercept).isTrue() - } - - @Test - @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) - fun shouldInterceptTouchEvent_dozing_touchInLockIconArea_touchNotIntercepted() { - // GIVEN dozing - whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND quick settings controller doesn't want it - whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(false) - // AND the lock icon wants the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(true) - - // THEN touch should NOT be intercepted by NotificationShade - assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isFalse() - } - - @Test @EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) fun shouldInterceptTouchEvent_dozing_touchNotInLockIconArea_touchIntercepted() { // GIVEN dozing whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller doesn't want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(false) + .thenReturn(false) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -483,14 +439,9 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : fun shouldInterceptTouchEvent_dozing_touchInStatusBar_touchIntercepted() { // GIVEN dozing whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) - // AND alternate bouncer doesn't want the touch - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller DOES want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(true) + .thenReturn(true) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -503,20 +454,13 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : whenever(sysuiStatusBarStateController.isDozing).thenReturn(true) // AND pulsing whenever(dozeServiceHost.isPulsing()).thenReturn(true) - // AND status bar doesn't want it - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(DOWN_EVENT)) - .thenReturn(false) - // AND shade is not fully expanded (mock is false by default) - // AND the lock icon does NOT want the touch - whenever(lockIconViewController.willHandleTouchWhileDozing(DOWN_EVENT)).thenReturn(false) // AND quick settings controller DOES want it whenever(quickSettingsController.shouldQuickSettingsIntercept(any(), any(), any())) - .thenReturn(true) + .thenReturn(true) // AND bouncer is not showing whenever(centralSurfaces.isBouncerShowing()).thenReturn(false) // AND panel view controller wants it - whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)) - .thenReturn(true) + whenever(shadeViewController.handleExternalInterceptTouch(DOWN_EVENT)).thenReturn(true) // THEN touch should be intercepted by NotificationShade assertThat(interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)).isTrue() @@ -589,12 +533,10 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : underTest.setupCommunalHubLayout() // Simluate attaching the view so flow collection starts. - val onAttachStateChangeListenerArgumentCaptor = ArgumentCaptor.forClass( - View.OnAttachStateChangeListener::class.java - ) - verify(view, atLeast(1)).addOnAttachStateChangeListener( - onAttachStateChangeListenerArgumentCaptor.capture() - ) + val onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java) + verify(view, atLeast(1)) + .addOnAttachStateChangeListener(onAttachStateChangeListenerArgumentCaptor.capture()) for (listener in onAttachStateChangeListenerArgumentCaptor.allValues) { listener.onViewAttachedToWindow(view) } @@ -608,7 +550,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : @RequiresFlagsDisabled(Flags.FLAG_COMMUNAL_HUB) fun doesNotSetupCommunalHubLayout_whenFlagDisabled() { whenever(mGlanceableHubContainerController.communalAvailable()) - .thenReturn(MutableStateFlow(false)) + .thenReturn(MutableStateFlow(false)) val mockCommunalPlaceholder = mock(View::class.java) val fakeViewIndex = 20 diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index ca29dd98a637..9093b2bcbbf8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -23,7 +23,6 @@ import android.widget.FrameLayout import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityContainerController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase @@ -57,7 +56,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider @@ -104,11 +102,9 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var notificationStackScrollLayoutController: NotificationStackScrollLayoutController - @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var shadeLogger: ShadeLogger @@ -161,7 +157,6 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags() featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) mSetFlagsRule.enableFlags(AConfigFlags.FLAG_REVAMPED_BOUNCER_MESSAGES) testScope = TestScope() controller = @@ -176,9 +171,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { panelExpansionInteractor, ShadeExpansionStateManager(), notificationStackScrollLayoutController, - statusBarKeyguardViewManager, statusBarWindowStateController, - lockIconViewController, centralSurfaces, dozeServiceHost, dozeScrimController, @@ -221,48 +214,18 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { } @Test - fun testInterceptTouchWhenShowingAltAuth() = - testScope.runTest { - captureInteractionEventHandler() - - // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept - whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())).thenReturn(true) - whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) - - // THEN we should intercept touch - assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue() - } - - @Test fun testNoInterceptTouch() = testScope.runTest { captureInteractionEventHandler() - // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept + // WHEN not dozing, drag down helper doesn't want to intercept whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.shouldInterceptTouchEvent(any())) - .thenReturn(false) whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) // THEN we shouldn't intercept touch assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse() } - @Test - fun testHandleTouchEventWhenShowingAltAuth() = - testScope.runTest { - captureInteractionEventHandler() - - // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept - whenever(statusBarStateController.isDozing).thenReturn(false) - whenever(statusBarKeyguardViewManager.onTouch(any())).thenReturn(true) - whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false) - - // THEN we should handle the touch - assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue() - } - private fun captureInteractionEventHandler() { verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) interactionEventHandler = interactionEventHandlerCaptor.value diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 905301eb38ae..943fb62003cb 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -74,6 +75,7 @@ class ShadeControllerImplTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var windowManager: WindowManager @@ -105,6 +107,8 @@ class ShadeControllerImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) whenever(windowManager.defaultDisplay).thenReturn(display) whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true) + whenever(statusBarWindowControllerStore.defaultDisplay) + .thenReturn(statusBarWindowController) shadeController = ShadeControllerImpl( commandQueue, @@ -113,7 +117,7 @@ class ShadeControllerImplTest : SysuiTestCase() { keyguardStateController, statusBarStateController, statusBarKeyguardViewManager, - statusBarWindowController, + statusBarWindowControllerStore, deviceProvisionedController, notificationShadeWindowController, 0, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt index 118dea6376bb..69a76271f726 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.packageManager import android.graphics.drawable.BitmapDrawable import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope +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.commandline.CommandRegistry @@ -40,13 +40,13 @@ import org.mockito.kotlin.any import org.mockito.kotlin.whenever @SmallTest -class DemoRonChipViewModelTest : SysuiTestCase() { +class DemoNotifChipViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry private val pw = PrintWriter(StringWriter()) - private val underTest = kosmos.demoRonChipViewModel + private val underTest = kosmos.demoNotifChipViewModel @Before fun setUp() { @@ -56,61 +56,61 @@ class DemoRonChipViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @DisableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_flagOff_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - addDemoRonChip() + addDemoNotifChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_noPackage_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-ron")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasPackage_shown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasText_shownWithText() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-ron", "-p", "com.android.systemui", "-t", "test") + arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"), ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java) } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_supportsColor() = testScope.runTest { val latest by collectLastValue(underTest.chip) commandRegistry.onShellCommand( pw, - arrayOf("demo-ron", "-p", "com.android.systemui", "-c", "#434343") + arrayOf("demo-notif", "-p", "com.android.systemui", "-c", "#434343"), ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) @@ -119,28 +119,28 @@ class DemoRonChipViewModelTest : SysuiTestCase() { } @Test - @EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) + @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun chip_hasHideArg_hidden() = testScope.runTest { val latest by collectLastValue(underTest.chip) // First, show a chip - addDemoRonChip() + addDemoNotifChip() assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) // Then, hide the chip - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "--hide")) + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide")) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } - private fun addDemoRonChip() { - Companion.addDemoRonChip(commandRegistry, pw) + private fun addDemoNotifChip() { + addDemoNotifChip(commandRegistry, pw) } companion object { - fun addDemoRonChip(commandRegistry: CommandRegistry, pw: PrintWriter) { - commandRegistry.onShellCommand(pw, arrayOf("demo-ron", "-p", "com.android.systemui")) + fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) { + commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui")) } } } 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 new file mode 100644 index 000000000000..eb5d9318c88f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.chips.notification.ui.viewmodel + +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.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +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 +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@SmallTest +@RunWith(AndroidJUnit4::class) +@OptIn(ExperimentalCoroutinesApi::class) +@EnableFlags(StatusBarNotifChips.FLAG_NAME) +class NotifChipsViewModelTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val activeNotificationListRepository = kosmos.activeNotificationListRepository + + private val underTest = kosmos.notifChipsViewModel + + @Test + fun chips_noNotifs_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs(emptyList()) + + assertThat(latest).isEmpty() + } + + @Test + fun chips_notifMissingStatusBarChipIconView_empty() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = null))) + + assertThat(latest).isEmpty() + } + + @Test + fun chips_oneNotif_statusBarIconViewMatches() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val icon = mock<StatusBarIconView>() + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon))) + + assertThat(latest).hasSize(1) + val chip = latest!![0] + assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon)) + } + + @Test + fun chips_twoNotifs_twoChips() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "notif1", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "notif2", statusBarChipIcon = secondIcon), + ) + ) + + assertThat(latest).hasSize(2) + assertIsNotifChip(latest!![0], firstIcon) + assertIsNotifChip(latest!![1], secondIcon) + } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + testScope.runCurrent() + } + + companion object { + fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) { + assertThat(latest) + .isInstanceOf(OngoingActivityChipModel.Shown.ShortTimeDelta::class.java) + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon)) + } + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 26ce7b956fde..e96def6d43a3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -25,7 +25,6 @@ import android.platform.test.annotations.DisableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue @@ -40,7 +39,8 @@ import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.phone.SystemUIDialog @@ -66,13 +66,11 @@ import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -/** - * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is disabled. - */ +/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@DisableFlags(FLAG_STATUS_BAR_RON_CHIPS) +@DisableFlags(StatusBarNotifChips.FLAG_NAME) class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = Kosmos().also { it.testCase = this } private val testScope = kosmos.testScope @@ -99,11 +97,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoRonChipViewModel.start() + kosmos.demoNotifChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } @@ -325,7 +323,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { latest: OngoingActivityChipModel?, chipView: View, dialog: SystemUIDialog, - kosmos: Kosmos + kosmos: Kosmos, ): DialogInterface.OnClickListener { // Capture the action that would get invoked when the user clicks "Stop" on the dialog lateinit var dialogStopAction: DialogInterface.OnClickListener diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt index 631120b39805..b12d7c57e1fd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithRonsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt @@ -24,7 +24,6 @@ import android.platform.test.annotations.EnableFlags import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope @@ -34,10 +33,13 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModelTest.Companion.addDemoRonChip -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer @@ -46,6 +48,10 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsVie import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.getStopActionFromDialog import com.android.systemui.statusbar.commandline.commandRegistry +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 +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel @@ -67,14 +73,12 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -/** - * Tests for [OngoingActivityChipsViewModel] when the [FLAG_STATUS_BAR_RON_CHIPS] flag is enabled. - */ +/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is enabled. */ @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(ExperimentalCoroutinesApi::class) -@EnableFlags(FLAG_STATUS_BAR_RON_CHIPS) -class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { +@EnableFlags(StatusBarNotifChips.FLAG_NAME) +class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock @@ -83,6 +87,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { private val screenRecordState = kosmos.screenRecordRepository.screenRecordState private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState private val callRepo = kosmos.ongoingCallRepository + private val activeNotificationListRepository = kosmos.activeNotificationListRepository private val pw = PrintWriter(StringWriter()) @@ -103,16 +108,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { @Before fun setUp() { setUpPackageManagerForMediaProjection(kosmos) - kosmos.demoRonChipViewModel.start() + kosmos.demoNotifChipViewModel.start() val icon = BitmapDrawable( context.resources, - Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(/* width= */ 100, /* height= */ 100, Bitmap.Config.ARGB_8888), ) whenever(kosmos.packageManager.getApplicationIcon(any<String>())).thenReturn(icon) } - // Even though the `primaryChip` flow isn't used when the RONs flag is on, still test that the + // Even though the `primaryChip` flow isn't used when the notifs flag is on, still test that the // flow has the right behavior to verify that we don't break any existing functionality. @Test @@ -249,13 +254,13 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) val latest by collectLastValue(underTest.chips) assertIsScreenRecordChip(latest!!.primary) assertIsCallChip(latest!!.secondary) - // Demo RON chip is dropped + // Demo notif chip is dropped } @Test @@ -288,10 +293,101 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { } @Test + fun chips_singleNotifChip_primaryIsNotifSecondaryIsHidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val icon = mock<StatusBarIconView>() + setNotifs(listOf(activeNotificationModel(key = "notif", statusBarChipIcon = icon))) + + assertIsNotifChip(latest!!.primary, icon) + assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + @Test + fun chips_twoNotifChips_primaryAndSecondaryAreNotifsInOrder() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon), + ) + ) + + assertIsNotifChip(latest!!.primary, firstIcon) + assertIsNotifChip(latest!!.secondary, secondIcon) + } + + @Test + fun chips_threeNotifChips_topTwoShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + val firstIcon = mock<StatusBarIconView>() + val secondIcon = mock<StatusBarIconView>() + val thirdIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel(key = "secondNotif", statusBarChipIcon = secondIcon), + activeNotificationModel(key = "thirdNotif", statusBarChipIcon = thirdIcon), + ) + ) + + assertIsNotifChip(latest!!.primary, firstIcon) + assertIsNotifChip(latest!!.secondary, secondIcon) + } + + @Test + fun chips_callAndNotifs_primaryIsCallSecondaryIsNotif() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + val firstIcon = mock<StatusBarIconView>() + setNotifs( + listOf( + activeNotificationModel(key = "firstNotif", statusBarChipIcon = firstIcon), + activeNotificationModel( + key = "secondNotif", + statusBarChipIcon = mock<StatusBarIconView>(), + ), + ) + ) + + assertIsCallChip(latest!!.primary) + assertIsNotifChip(latest!!.secondary, firstIcon) + } + + @Test + fun chips_screenRecordAndCallAndNotifs_notifsNotShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chips) + + callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + screenRecordState.value = ScreenRecordModel.Recording + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = mock<StatusBarIconView>(), + ) + ) + ) + + assertIsScreenRecordChip(latest!!.primary) + assertIsCallChip(latest!!.secondary) + } + + @Test fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -299,7 +395,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.primaryChip) - assertIsDemoRonChip(latest) + assertIsDemoNotifChip(latest) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) @@ -333,7 +429,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) val latest by collectLastValue(underTest.primaryChip) @@ -355,15 +451,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { // WHEN the higher priority call is removed callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN the lower priority demo RON is used - assertIsDemoRonChip(latest) + // THEN the lower priority demo notif is used + assertIsDemoNotifChip(latest) } @Test fun chips_movesChipsAroundAccordingToPriority() = testScope.runTest { // Start with just the lowest priority chip shown - addDemoRonChip(commandRegistry, pw) + addDemoNotifChip(commandRegistry, pw) // And everything else hidden callRepo.setOngoingCallState(OngoingCallModel.NoCall) mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -371,16 +467,16 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) - assertIsDemoRonChip(latest!!.primary) + assertIsDemoNotifChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN the higher priority call chip is added callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - // THEN the higher priority call chip is used as primary and demo ron is demoted to + // THEN the higher priority call chip is used as primary and demo notif is demoted to // secondary assertIsCallChip(latest!!.primary) - assertIsDemoRonChip(latest!!.secondary) + assertIsDemoNotifChip(latest!!.secondary) // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -391,7 +487,7 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { ) // THEN the higher priority media projection chip is used as primary and call is demoted - // to secondary (and demo RON is dropped altogether) + // to secondary (and demo notif is dropped altogether) assertIsShareToAppChip(latest!!.primary) assertIsCallChip(latest!!.secondary) @@ -405,15 +501,15 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing callRepo.setOngoingCallState(OngoingCallModel.NoCall) - // THEN media projection and demo RON remain + // THEN media projection and demo notif remain assertIsShareToAppChip(latest!!.primary) - assertIsDemoRonChip(latest!!.secondary) + assertIsDemoNotifChip(latest!!.secondary) // WHEN media projection is dropped mediaProjectionState.value = MediaProjectionState.NotProjecting - // THEN demo RON is promoted to primary - assertIsDemoRonChip(latest!!.primary) + // THEN demo notif is promoted to primary + assertIsDemoNotifChip(latest!!.primary) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @@ -526,9 +622,17 @@ class OngoingActivityChipsWithRonsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false)) } - private fun assertIsDemoRonChip(latest: OngoingActivityChipModel?) { + private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).icon) .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java) } + + private fun setNotifs(notifs: List<ActiveNotificationModel>) { + activeNotificationListRepository.activeNotifications.value = + ActiveNotificationsStore.Builder() + .apply { notifs.forEach { addIndividualNotif(it) } } + .build() + testScope.runCurrent() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index 9142972eabdd..f64387c95e3d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Assert.assertThrows @@ -39,7 +40,8 @@ import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class StatusBarInitializerTest : SysuiTestCase() { - val windowController = mock(StatusBarWindowController::class.java) + private val windowController = mock(StatusBarWindowController::class.java) + private val windowControllerStore = mock(StatusBarWindowControllerStore::class.java) @Before fun setup() { @@ -52,15 +54,16 @@ class StatusBarInitializerTest : SysuiTestCase() { whenever(fragmentHostManager.fragmentManager).thenReturn(fragmentManager) whenever(fragmentManager.beginTransaction()).thenReturn(transaction) whenever(transaction.replace(any(), any(), any())).thenReturn(transaction) - + whenever(windowControllerStore.defaultDisplay).thenReturn(windowController) whenever(windowController.fragmentHostManager).thenReturn(fragmentHostManager) } val underTest = StatusBarInitializerImpl( - windowController, - { mock(CollapsedStatusBarFragment::class.java) }, - setOf(), + displayId = context.displayId, + statusBarWindowControllerStore = windowControllerStore, + collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) }, + creationListeners = setOf(), ) @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt index 580336539c37..bb3fb1e71f78 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController import com.android.systemui.statusbar.window.data.model.StatusBarWindowState import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.fakeStatusBarWindowController +import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore import com.android.systemui.testKosmos import com.android.wm.shell.bubbles.bubbles import com.google.common.truth.Truth.assertThat @@ -67,7 +67,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { } private val testScope = kosmos.testScope private val statusBarViewController = kosmos.mockPhoneStatusBarViewController - private val statusBarWindowController = kosmos.fakeStatusBarWindowController + private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider private val notificationShadeWindowViewController = @@ -94,7 +94,7 @@ class StatusBarOrchestratorTest : SysuiTestCase() { fun start_attachesWindow() { orchestrator.start() - assertThat(statusBarWindowController.isAttached).isTrue() + assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt index 984bda1c0d21..a629b2447921 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.animation.AnimatorTestRule import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture @@ -53,6 +54,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule(this) @Mock private lateinit var sbWindowController: StatusBarWindowController + @Mock private lateinit var sbWindowControllerStore: StatusBarWindowControllerStore @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider private var testView = TestView(mContext) @@ -61,7 +63,7 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - + whenever(sbWindowControllerStore.defaultDisplay).thenReturn(sbWindowController) // StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to // ensure that the chip view is added to a parent view whenever(sbWindowController.addViewToWindow(any(), any())).then { @@ -93,8 +95,8 @@ class SystemEventChipAnimationControllerTest : SysuiTestCase() { controller = SystemEventChipAnimationController( context = mContext, - statusBarWindowController = sbWindowController, - contentInsetsProvider = insetsProvider + statusBarWindowControllerStore = sbWindowControllerStore, + contentInsetsProvider = insetsProvider, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt index 35e4047109d5..97fa6eb17b5b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationScrimNestedScrollConnectionTest.kt @@ -31,6 +31,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { private var isStarted = false + private var wasStarted = false private var scrimOffset = 0f private var contentHeight = 0f private var isCurrentGestureOverscroll = false @@ -46,7 +47,10 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { minVisibleScrimHeight = { MIN_VISIBLE_SCRIM_HEIGHT }, isCurrentGestureOverscroll = { isCurrentGestureOverscroll }, onStart = { isStarted = true }, - onStop = { isStarted = false }, + onStop = { + wasStarted = true + isStarted = false + }, ) @Test @@ -180,6 +184,7 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) + assertThat(wasStarted).isEqualTo(false) assertThat(isStarted).isEqualTo(false) } @@ -196,7 +201,9 @@ class NotificationScrimNestedScrollConnectionTest : SysuiTestCase() { ) assertThat(offsetConsumed).isEqualTo(Offset.Zero) - assertThat(isStarted).isEqualTo(true) + // Returning 0 offset will immediately stop the connection + assertThat(wasStarted).isEqualTo(true) + assertThat(isStarted).isEqualTo(false) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index 8d678ef00b4a..bf144729dea3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -21,7 +21,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest -import com.android.app.tracing.coroutines.flow.map import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.DisableSceneContainer @@ -51,6 +50,7 @@ import com.android.systemui.util.ui.isAnimating import com.android.systemui.util.ui.value import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt index 179799503ac0..bac79a9cc520 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -94,6 +95,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { @Mock private lateinit var activityTransitionAnimator: ActivityTransitionAnimator @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock private lateinit var keyguardStateController: KeyguardStateController @@ -112,6 +114,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + `when`(statusBarWindowControllerStore.defaultDisplay).thenReturn(statusBarWindowController) underTest = LegacyActivityStarterInternalImpl( centralSurfacesOptLazy = { Optional.of(centralSurfaces) }, @@ -128,7 +131,7 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() { context = context, displayId = DISPLAY_ID, lockScreenUserManager = lockScreenUserManager, - statusBarWindowController = statusBarWindowController, + statusBarWindowControllerStore = statusBarWindowControllerStore, wakefulnessLifecycle = wakefulnessLifecycle, keyguardStateController = keyguardStateController, statusBarStateController = statusBarStateController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 1d74331e429b..94753f7e5203 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -34,7 +34,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -43,9 +42,7 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; -import android.service.trust.TrustAgentService; import android.testing.TestableLooper; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; @@ -67,7 +64,6 @@ import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; @@ -580,22 +576,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @DisableSceneContainer - @DisableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testShowAlternateBouncer_unlockingWithBiometricAllowed() { - // GIVEN will show alternate bouncer - when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false); - when(mAlternateBouncerInteractor.show()).thenReturn(true); - - // WHEN showGenericBouncer is called - mStatusBarKeyguardViewManager.showBouncer(true); - - // THEN alt auth bouncer is shown - verify(mAlternateBouncerInteractor).show(); - verify(mPrimaryBouncerInteractor, never()).show(anyBoolean()); - } - - @Test public void testUpdateResources_delegatesToBouncer() { mStatusBarKeyguardViewManager.updateResources(); @@ -841,145 +821,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_alternateBouncerViewFlagEnabled() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN the touch is not acted upon - verify(mCallback, never()).onTouch(any()); - } - - @Test - @EnableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void onInterceptTouch_alternateBouncerViewFlagEnabled() { - // GIVEN alternate bouncer view flag enabled & the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN the touch is not intercepted - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - } - - @Test - public void handleDispatchTouchEvent_alternateBouncerNotVisible() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // THEN handleDispatchTouchEvent doesn't use the touches - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is not acted upon - verify(mCallback, never()).onTouch(any()); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_shouldInterceptTouchAndHandleTouch() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN all touches are NOT the udfps overlay - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent - // to its child views (handleDispatchTouchEvent returns true) - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is acted upon once for each dispatchTOuchEvent call - verify(mCallback, times(3)).onTouch(any()); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void handleDispatchTouchEvent_shouldInterceptTouchButNotHandleTouch() { - mStatusBarKeyguardViewManager.addCallback(mCallback); - - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN all touches are within the udfps overlay - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(true); - - // THEN handleDispatchTouchEvent eats/intercepts the touches so motion events aren't sent - // to its child views (handleDispatchTouchEvent returns true) - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.dispatchTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - - // THEN the touch is NOT acted upon at the moment - verify(mCallback, never()).onTouch(any()); - } - - @Test - @DisableSceneContainer - public void shouldInterceptTouch_alternateBouncerNotVisible() { - // GIVEN the alternate bouncer is not visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // THEN no motion events are intercepted - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertFalse(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void shouldInterceptTouch_alternateBouncerVisible() { - // GIVEN the alternate bouncer is visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // THEN all motion events are intercepted - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0) - )); - assertTrue(mStatusBarKeyguardViewManager.shouldInterceptTouchEvent( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - )); - } - - @Test public void alternateBouncerToShowPrimaryBouncer_updatesScrimControllerOnce() { // GIVEN the alternate bouncer has shown and calls to hide() will result in successfully // hiding it @@ -997,106 +838,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void alternateBouncerOnTouch_actionDownThenUp_noMinTimeShown_noHideAltBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(false); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN ACTION_DOWN and ACTION_UP touch event comes - boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); - when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true); - boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the touches are handled (doesn't let touches through to underlying views) - assertTrue(touchHandledDown); - assertTrue(touchHandledUp); - - // THEN alternate bouncer does NOT attempt to hide since min showing time wasn't met - verify(mAlternateBouncerInteractor, never()).hide(); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void alternateBouncerOnTouch_actionDownThenUp_handlesTouch_hidesAltBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN ACTION_DOWN and ACTION_UP touch event comes - boolean touchHandledDown = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)); - when(mAlternateBouncerInteractor.getReceivedDownTouch()).thenReturn(true); - boolean touchHandledUp = mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the touches are handled - assertTrue(touchHandledDown); - assertTrue(touchHandledUp); - - // THEN alternate bouncer attempts to hide - verify(mAlternateBouncerInteractor).hide(); - } - - @Test - @DisableSceneContainer - public void alternateBouncerOnTouch_actionUp_doesNotHideAlternateBouncer() { - reset(mAlternateBouncerInteractor); - - // GIVEN the alternate bouncer has shown for a minimum amount of time - when(mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()).thenReturn(true); - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(any())).thenReturn(false); - - // WHEN only ACTION_UP touch event comes - mStatusBarKeyguardViewManager.onTouch( - MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)); - - // THEN the alternateBouncer doesn't hide - verify(mAlternateBouncerInteractor, never()).hide(); - } - - @Test - @DisableSceneContainer - @DisableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() { - // GIVEN keyguard update monitor callback is registered - verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); - - reset(mKeyguardUpdateMonitor); - reset(mKeyguardMessageAreaController); - - // GIVEN alternate bouncer state = not visible - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - - // WHEN the device is trusted by active unlock - mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser( - true, - true, - new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD - | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE), - null - ); - - // THEN the false visibility state is propagated to the keyguardUpdateMonitor - verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false)); - - // THEN message area visibility updated to FALSE with empty message - verify(mKeyguardMessageAreaController).setIsVisible(eq(false)); - verify(mKeyguardMessageAreaController).setMessage(eq("")); - } - - @Test - @DisableSceneContainer @DisableFlags(Flags.FLAG_SIM_PIN_RACE_CONDITION_ON_RESTART) public void testShowBouncerOrKeyguard_needsFullScreen() { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt index 597e2e45ea14..e0d9fac0eba5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaListenerTest.kt @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.domain.interactor.activeNotif 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.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock @@ -74,6 +75,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever private const val CALL_UID = 900 @@ -106,6 +108,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { @Mock private lateinit var mockActivityStarter: ActivityStarter @Mock private lateinit var mockIActivityManager: IActivityManager @Mock private lateinit var mockStatusBarWindowController: StatusBarWindowController + @Mock private lateinit var mockStatusBarWindowControllerStore: StatusBarWindowControllerStore private lateinit var chipView: View @@ -118,6 +121,8 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) val notificationCollection = mock(CommonNotifCollection::class.java) + whenever(mockStatusBarWindowControllerStore.defaultDisplay) + .thenReturn(mockStatusBarWindowController) controller = OngoingCallController( @@ -131,7 +136,7 @@ class OngoingCallControllerViaListenerTest : SysuiTestCase() { mainExecutor, mockIActivityManager, DumpManager(), - mockStatusBarWindowController, + mockStatusBarWindowControllerStore, mockSwipeStatusBarAwayGestureHandler, statusBarModeRepository, logcatLogBuffer("OngoingCallControllerViaListenerTest"), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt index dfe01bf45f38..2ad50cc38b7c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerViaRepoTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.statusbar.notification.shared.CallType 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.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -93,6 +94,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { private val mockActivityStarter = kosmos.activityStarter private val mockIActivityManager = mock<IActivityManager>() private val mockStatusBarWindowController = mock<StatusBarWindowController>() + private val mockStatusBarWindowControllerStore = mock<StatusBarWindowControllerStore>() private lateinit var chipView: View @@ -103,6 +105,8 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null) } + whenever(mockStatusBarWindowControllerStore.defaultDisplay) + .thenReturn(mockStatusBarWindowController) controller = OngoingCallController( testScope.backgroundScope, @@ -115,7 +119,7 @@ class OngoingCallControllerViaRepoTest : SysuiTestCase() { mainExecutor, mockIActivityManager, DumpManager(), - mockStatusBarWindowController, + mockStatusBarWindowControllerStore, mockSwipeStatusBarAwayGestureHandler, statusBarModeRepository, logcatLogBuffer("OngoingCallControllerViaRepoTest"), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt new file mode 100644 index 000000000000..4a53a7a74bad --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartableTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import android.app.AutomaticZenRule +import android.app.NotificationManager +import android.net.Uri +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.backgroundCoroutineContext +import com.android.systemui.kosmos.testScope +import com.android.systemui.testKosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +@SmallTest +class ZenModesCleanupStartableTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + + @Mock private lateinit var notificationManager: NotificationManager + + private lateinit var underTest: ZenModesCleanupStartable + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + underTest = + ZenModesCleanupStartable( + testScope.backgroundScope, + kosmos.backgroundCoroutineContext, + notificationManager, + ) + } + + @Test + fun start_withGamingModeZenRule_deletesIt() = + testScope.runTest { + whenever(notificationManager.automaticZenRules) + .thenReturn( + mutableMapOf( + Pair( + "gaming", + AutomaticZenRule.Builder( + "Gaming Mode", + Uri.parse( + "android-app://com.android.systemui/game-mode-dnd-controller" + ), + ) + .setPackage("com.android.systemui") + .build(), + ), + Pair( + "other", + AutomaticZenRule.Builder("Other Mode", Uri.parse("something-else")) + .setPackage("com.other.package") + .build(), + ), + ) + ) + + underTest.start() + runCurrent() + + verify(notificationManager).removeAutomaticZenRule(eq("gaming")) + } + + @Test + fun start_withoutGamingModeZenRule_doesNothing() = + testScope.runTest { + whenever(notificationManager.automaticZenRules) + .thenReturn( + mutableMapOf( + Pair( + "other", + AutomaticZenRule.Builder("Other Mode", Uri.parse("something-else")) + .setPackage("com.android.systemui") + .build(), + ) + ) + ) + + underTest.start() + runCurrent() + + verify(notificationManager, never()).removeAutomaticZenRule(any()) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt index b8f581574848..a4b3916b7a55 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt @@ -35,7 +35,7 @@ class DrawableSizeTest : SysuiTestCase() { val drawable = BitmapDrawable( resources, - Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888), ) val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300) assertThat(result).isSameInstanceAs(drawable) @@ -48,7 +48,7 @@ class DrawableSizeTest : SysuiTestCase() { val drawable = BitmapDrawable( resources, - Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888) + Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888), ) val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75) @@ -64,4 +64,31 @@ class DrawableSizeTest : SysuiTestCase() { val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) assertThat(result).isSameInstanceAs(drawable) } + + @Test + fun testDownscaleToSize_layerDrawable_allLayersSameType_resized() { + val drawable = + resources.getDrawable( + com.android.systemui.tests.R.drawable.layer_drawable_all_same_type, + resources.newTheme(), + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + + assertThat(result).isNotSameInstanceAs(drawable) + } + + /** Regression test for b/244282477. */ + @Test + fun testDownscaleToSize_layerDrawable_layersAreDifferentTypes_unchanged() { + val drawable = + resources.getDrawable( + com.android.systemui.tests.R.drawable.layer_drawable_different_types, + resources.newTheme(), + ) + + val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1) + + assertThat(result).isSameInstanceAs(drawable) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt index 06a3e8b0a766..31d2eb37dead 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt @@ -44,7 +44,7 @@ private val dialogTimeoutDuration = 3.seconds @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper() +@TestableLooper.RunWithLooper class VolumeDialogVisibilityInteractorTest : SysuiTestCase() { private val kosmos: Kosmos = testKosmos() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt new file mode 100644 index 000000000000..7c5a48728a11 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.domain.interactor + +import android.content.packageManager +import android.content.pm.PackageManager +import android.media.AudioManager +import android.testing.TestableLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.VolumeDialogController +import com.android.systemui.plugins.fakeVolumeDialogController +import com.android.systemui.testKosmos +import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.whenever + +private const val AUDIO_SHARING_STREAM = 99 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class VolumeDialogSlidersInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private lateinit var underTest: VolumeDialogSlidersInteractor + + private var isTv: Boolean = false + + @Before + fun setUp() { + with(kosmos) { + whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)).thenAnswer { + isTv + } + + underTest = kosmos.volumeDialogSlidersInteractor + } + } + + @Test + fun activeStreamIsSlider() = + with(kosmos) { + testScope.runTest { + runCurrent() + fakeVolumeDialogController.updateState { + activeStream = AudioManager.STREAM_SYSTEM + states.put(AudioManager.STREAM_MUSIC, buildStreamState()) + states.put(AudioManager.STREAM_SYSTEM, buildStreamState()) + } + + val slidersModel by collectLastValue(underTest.sliders) + runCurrent() + + assertThat(slidersModel!!.slider) + .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM)) + assertThat(slidersModel!!.floatingSliders) + .isEqualTo(listOf(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC))) + } + } + + @Test + fun streamsOrder() = + with(kosmos) { + testScope.runTest { + runCurrent() + fakeVolumeDialogController.onAccessibilityModeChanged(true) + fakeVolumeDialogController.updateState { + activeStream = AudioManager.STREAM_MUSIC + states.put(AUDIO_SHARING_STREAM, buildStreamState { dynamic = true }) + states.put(AUDIO_SHARING_STREAM + 1, buildStreamState { dynamic = true }) + states.put(AudioManager.STREAM_MUSIC, buildStreamState()) + states.put(AudioManager.STREAM_ACCESSIBILITY, buildStreamState()) + } + + val slidersModel by collectLastValue(underTest.sliders) + runCurrent() + + assertThat(slidersModel!!.slider) + .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC)) + assertThat(slidersModel!!.floatingSliders) + .isEqualTo( + listOf( + VolumeDialogSliderType.Stream(AudioManager.STREAM_ACCESSIBILITY), + VolumeDialogSliderType.AudioSharingStream(AUDIO_SHARING_STREAM), + VolumeDialogSliderType.RemoteMediaStream(AUDIO_SHARING_STREAM + 1), + ) + ) + } + } + + @Test + fun accessibilityStreamDisabled_filteredOut() = + with(kosmos) { + testScope.runTest { + runCurrent() + fakeVolumeDialogController.onAccessibilityModeChanged(false) + fakeVolumeDialogController.updateState { + states.put(AudioManager.STREAM_ACCESSIBILITY, buildStreamState()) + states.put(AudioManager.STREAM_MUSIC, buildStreamState()) + } + + val slidersModel by collectLastValue(underTest.sliders) + runCurrent() + + assertThat(slidersModel!!.slider) + .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC)) + assertThat(slidersModel!!.floatingSliders).isEmpty() + } + } + + @Test + fun isTv_onlyActiveStream() = + with(kosmos) { + testScope.runTest { + runCurrent() + isTv = true + fakeVolumeDialogController.updateState { + activeStream = AudioManager.STREAM_SYSTEM + states.put(AudioManager.STREAM_MUSIC, buildStreamState()) + states.put(AudioManager.STREAM_SYSTEM, buildStreamState()) + } + + val slidersModel by collectLastValue(underTest.sliders) + runCurrent() + + assertThat(slidersModel!!.slider) + .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM)) + assertThat(slidersModel!!.floatingSliders).isEmpty() + } + } + + private fun buildStreamState( + build: VolumeDialogController.StreamState.() -> Unit = {} + ): VolumeDialogController.StreamState { + return VolumeDialogController.StreamState().apply(build) + } +} diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index fc9c917c152b..8bef4759c55d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -26,7 +26,7 @@ android:clipChildren="false" android:layout_gravity="center_horizontal|top"> <com.android.keyguard.KeyguardClockFrame - android:id="@+id/lockscreen_clock_view" + android:id="@id/lockscreen_clock_view" android:layout_width="wrap_content" android:layout_height="@dimen/small_clock_height" android:layout_alignParentStart="true" @@ -35,7 +35,7 @@ android:paddingStart="@dimen/clock_padding_start" android:visibility="invisible" /> <com.android.keyguard.KeyguardClockFrame - android:id="@+id/lockscreen_clock_view_large" + android:id="@id/lockscreen_clock_view_large" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 042067b12e8a..fcb3a3ec8edc 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -20,7 +20,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"اپنا PIN درج کریں"</string> + <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"اپنا PIN درج کریں"</string> <string name="keyguard_enter_pin" msgid="8114529922480276834">"PIN درج کریں"</string> <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"اپنا پیٹرن درج کریں"</string> <string name="keyguard_enter_pattern" msgid="7616595160901084119">"پیٹرن ڈرا کریں"</string> diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml index 690a89a044b7..d0a1ce8ae629 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml @@ -45,31 +45,26 @@ android:tint="?android:attr/colorPrimary" /> - <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text] will ever - be shown at one time. --> + <!-- Only one of [ongoing_activity_chip_time, ongoing_activity_chip_text, + ongoing_activity_chip_short_time_delta] will ever be shown at one time. --> + + <!-- Shows a timer, like 00:01. --> <com.android.systemui.statusbar.chips.ui.view.ChipChronometer android:id="@+id/ongoing_activity_chip_time" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" - android:textAppearance="@android:style/TextAppearance.Material.Small" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:textColor="?android:attr/colorPrimary" + style="@style/StatusBar.Chip.Text" /> - <!-- Used to show generic text in the chip instead of a timer. --> + <!-- Shows generic text. --> <TextView android:id="@+id/ongoing_activity_chip_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:gravity="center|start" - android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding" - android:textAppearance="@android:style/TextAppearance.Material.Small" - android:fontFamily="@*android:string/config_headlineFontFamily" - android:textColor="?android:attr/colorPrimary" + style="@style/StatusBar.Chip.Text" + android:visibility="gone" + /> + + <!-- Shows a time delta in short form, like "15min" or "1hr". --> + <android.widget.DateTimeView + android:id="@+id/ongoing_activity_chip_short_time_delta" + style="@style/StatusBar.Chip.Text" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 22d34eb7b115..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -58,22 +58,22 @@ android:layout_height="match_parent" android:visibility="invisible" /> - <!-- Shared container for the notification stack. Can be positioned by either - the keyguard_root_view or notification_panel --> - <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer - android:id="@+id/shared_notification_container" + <!-- Root for all keyguard content. It was previously located within the shade. --> + <com.android.systemui.keyguard.ui.view.KeyguardRootView + android:id="@id/keyguard_root_view" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" - android:clipToPadding="false" /> - <!-- Root for all keyguard content. It was previously located within the shade. --> - <com.android.systemui.keyguard.ui.view.KeyguardRootView - android:id="@id/keyguard_root_view" + <!-- Shared container for the notification stack. Can be positioned by either + the keyguard_root_view or notification_panel --> + <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer + android:id="@+id/shared_notification_container" android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false" + android:clipToPadding="false" /> <include layout="@layout/brightness_mirror_container" /> diff --git a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml b/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml deleted file mode 100644 index 4799f8c5b668..000000000000 --- a/packages/SystemUI/res/layout/udfps_fpm_empty_view.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<com.android.systemui.biometrics.UdfpsFpmEmptyView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/udfps_animation_view" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- The layout height/width are placeholders, which will be overwritten by - FingerprintSensorPropertiesInternal. --> - <View - android:id="@+id/udfps_enroll_accessibility_view" - android:layout_gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:contentDescription="@string/accessibility_fingerprint_label"/> -</com.android.systemui.biometrics.UdfpsFpmEmptyView> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 0f411ca5bfea..62bff957c61e 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Op • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Af"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nie gestel nie"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Bestuur in instellings"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Geen aktiewe modusse nie}=1{{mode} is aktief}other{# modusse is aktief}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Jy sal nie deur geluide en vibrasies gepla word nie, behalwe deur wekkers, herinneringe, geleenthede en bellers wat jy spesifiseer. Jy sal steeds enigiets hoor wat jy kies om te speel, insluitend musiek, video\'s en speletjies."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Onbeskikbaar omdat luitoon gedemp is"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Onbeskikbaar want Moenie Steur Nie is aan"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Onbeskikbaar want Moenie Steur Nie is aan"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om te ontdemp."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om op vibreer te stel. Toeganklikheidsdienste kan dalk gedemp wees."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te demp. Toeganklikheidsdienste kan dalk gedemp wees."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Leer raakpaneelgebare"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeer met jou sleutelbord en raakpaneel"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer raakpaneelgebare, kortpadsleutels en meer"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gaan terug"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gaan na tuisskerm"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Bekyk onlangse apps"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gaan terug"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swiep links of regs met drie vingers op jou raakpaneel"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Mooi so!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Jy het die Gaan Terug-gebaar voltooi."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gaan na tuisskerm"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swiep op met drie vingers op jou raakpaneel"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Uitstekende werk!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Jy het die Gaan na Tuisskerm-gebaar voltooi"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Bekyk onlangse apps"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swiep op en hou met drie vingers op jou raakpaneel"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Knap gedaan!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jy het die Bekyk Onlangse Apps-gebaar voltooi."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Bekyk alle apps"</string> + <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> <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 553142448330..74e072b3e0e0 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"አልተቀናበረም"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"በቅንብሮች ውስጥ አስተዳድር"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ምንም ገቢር ሁነታዎች የሉም}=1{{mode} ገቢር ነው}one{# ሁኔታ ገቢር ነው}other{# ሁኔታዎች ገቢር ናቸው}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"እርስዎ ከወሰንዋቸው ማንቂያዎች፣ አስታዋሾች፣ ክስተቶች እና ደዋዮች በስተቀር፣ በድምጾች እና ንዝረቶች አይረበሹም። ሙዚቃ፣ ቪዲዮዎች እና ጨዋታዎች ጨምሮ ለመጫወት የሚመርጡትን ማንኛውም ነገር አሁንም ይሰማሉ።"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"የጥሪ ድምጽ ስለተዘጋ አይገኝም"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"አትረብሽ ስለበራ አይገኝም"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"አትረብሽ ስለበራ አይገኝም"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s። ወደ ንዝረት ለማቀናበር መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s። ድምጸ-ከል ለማድረግ መታ ያድርጉ። የተደራሽነት አገልግሎቶች ድምጸ-ከል ሊደረግባቸው ይችላል።"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"የመዳሰሻ ሰሌዳ ምልክቶችን ይወቁ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"የእርስዎን የቁልፍ ሰሌዳ እና የመዳሰሻ ሰሌዳ በመጠቀም ያስሱ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"የመዳሰሻ ሰሌዳ ምልክቶችን፣ የቁልፍ ሰሌዳ አቋራጮችን እና ሌሎችን ይወቁ"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ወደኋላ ተመለስ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ወደ መነሻ ሂድ"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ተከናውኗል"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ወደኋላ ተመለስ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደ ግራ ወይም ወደ ቀኝ ያንሸራትቱ"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"አሪፍ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ወደኋላ የመመለስ ምልክትን አጠናቅቀዋል።"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ወደ መነሻ ሂድ"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"በመዳሰሻ ሰሌዳዎ ላይ በሦስት ጣቶች ወደ ላይ ያንሸራትቱ"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ጥሩ ሥራ!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ወደ መነሻ ሂድ ምልክትን አጠናቅቀዋል"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"የቅርብ ጊዜ መተግበሪያዎችን አሳይ"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"የመዳሰሻ ሰሌዳዎ ላይ ሦስት ጣቶችን በመጠቀም ወደላይ ያንሸራትቱ እና ይያዙ"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ጥሩ ሠርተዋል!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"የቅርብ ጊዜ መተግበሪያዎች አሳይ ምልክትን አጠናቅቀዋል።"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"ሁሉንም መተግበሪያዎች ይመልከቱ"</string> + <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="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 091b58e9f414..922caeaf558d 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"لم يتم ضبط الوضع"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"الإدارة في الإعدادات"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ما مِن أوضاع مفعَّلة}=1{الوضع \"{mode}\" مفعَّل}two{وضعان مفعَّلان}few{# أوضاع مفعَّلة}many{# وضعًا مفعَّلاً}other{# وضع مفعَّل}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"لن يتم إزعاجك بالأصوات والاهتزاز، باستثناء المُنبِّهات والتذكيرات والأحداث والمتصلين الذين تحددهم. وسيظل بإمكانك سماع أي عناصر أخرى تختار تشغيلها، بما في ذلك الموسيقى والفيديوهات والألعاب."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"غير متاح بسبب كتم صوت الرنين"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"مستوى الصوت غير متاح بسبب تفعيل وضع \"عدم الإزعاج\""</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"مستوى الصوت غير متاح لأنّ وضع \"عدم الإزعاج\" مفعّل"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. انقر لإلغاء التجاهل."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. انقر للتعيين على الاهتزاز. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. انقر للتجاهل. قد يتم تجاهل خدمات \"سهولة الاستخدام\"."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"تعرَّف على إيماءات لوحة اللمس"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"التنقّل باستخدام لوحة المفاتيح ولوحة اللمس"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"تعرَّف على إيماءات لوحة اللمس واختصارات لوحة المفاتيح والمزيد"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"رجوع"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"الانتقال إلى الصفحة الرئيسية"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"عرض التطبيقات المستخدَمة مؤخرًا"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تم"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"رجوع"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"مرِّر سريعًا لليمين أو لليسار باستخدام 3 أصابع على لوحة اللمس"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"أحسنت."</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"لقد أكملت التدريب على إيماءة الرجوع."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"الانتقال إلى الشاشة الرئيسية"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"مرّر سريعًا للأعلى باستخدام 3 أصابع على لوحة اللمس"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"أحسنت صنعًا."</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"لقد أكملت الدليل التوجيهي عن إيماءة \"الانتقال إلى الشاشة الرئيسية\""</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"عرض التطبيقات المستخدَمة مؤخرًا"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"مرِّر سريعًا للأعلى مع الاستمرار باستخدام 3 أصابع على لوحة اللمس"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"أحسنت."</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"لقد أكملْت الدليل التوجيهي على إيماءة \"عرض التطبيقات المستخدَمة مؤخرًا\"."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"عرض جميع التطبيقات"</string> + <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="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 bbaf97450cc2..f353428b2bb7 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"ছেট কৰা হোৱা নাই"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ছেটিঙত পৰিচালনা কৰক"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{কোনো সক্ৰিয় ম’ড নাই}=1{{mode} সক্ৰিয় আছে}one{# টা ম’ড সক্ৰিয় আছে}other{# টা ম’ড সক্ৰিয় আছে}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ৰিং মিউট কৰি থোৱাৰ বাবে উপলব্ধ নহয়"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"অসুবিধা নিদিব অন থকাৰ কাৰণে উপলব্ধ নহয়"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"অসুবিধা নিদিব অন থকাৰ কাৰণে উপলব্ধ নহয়"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। আনমিউট কৰিবৰ বাবে টিপক।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পনৰ বাবে টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট কৰিবলৈ টিপক। দিব্য়াংগসকলৰ বাবে থকা সেৱা মিউট হৈ থাকিব পাৰে।"</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"ডেম\' ম\'ড দেখুৱাওক"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"ইথাৰনেট"</string> <string name="status_bar_alarm" msgid="87160847643623352">"এলাৰ্ম"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> অন আছে"</string> <string name="wallet_title" msgid="5369767670735827105">"ৱালেট"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"আপোনাৰ ফ’নটোৰে দ্ৰুত তথা অধিক সুৰক্ষিত ক্ৰয় কৰিবলৈ ছেট আপ পাওক"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"আটাইবোৰ দেখুৱাওক"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"টাচ্চপেডৰ নিৰ্দেশসমূহ জানক"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"আপোনাৰ কীব’ৰ্ড আৰু টাচ্চপেড ব্যৱহাৰ কৰি নেভিগে’ট কৰক"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"টাচ্চপেডৰ নিৰ্দেশ, কীব’ৰ্ডৰ শ্বৰ্টকাট আৰু অধিকৰ বিষয়ে জানক"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"উভতি যাওক"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"গৃহ পৃষ্ঠালৈ যাওক"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"শেহতীয়া এপ্সমূহ চাওক"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"হ’ল"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"উভতি যাওক"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি বাওঁফাললৈ বা সোঁফাললৈ ছোৱাইপ কৰক"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"সুন্দৰ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"আপুনি উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"গৃহ পৃষ্ঠালৈ যাওক"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপোনাৰ টাচ্চপেডৰ তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"বঢ়িয়া!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"শেহতীয়া এপ্সমূহ চাওক"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলি ব্যৱহাৰ কৰি ওপৰলৈ ছোৱাইপ কৰি ধৰি ৰাখক"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"বঢ়িয়া!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"আপুনি শেহতীয়া এপ্ চোৱাৰ নিৰ্দেশনাটো সম্পূৰ্ণ কৰিছে।"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"আটাইবোৰ এপ্ চাওক"</string> + <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="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 211a05d8b08d..6d2ec46ed831 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Aktiv"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktiv • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Deaktiv"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ayarlanmayıb"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Ayarlarda idarə edin"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aktiv rejim yoxdur}=1{{mode} aktivdir}other{# rejim aktivdir}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Seçdiyiniz siqnal, xatırladıcı, tədbir və zənglər istisna olmaqla səslər və vibrasiyalar Sizi narahat etməyəcək. Musiqi, video və oyunlar da daxil olmaqla oxutmaq istədiyiniz hər şeyi eşidəcəksiniz."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zəng səssiz edildiyi üçün əlçatan deyil"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Narahat Etməyin aktiv olduğu üçün əlçatan deyil"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Narahat Etməyin aktiv olduğu üçün əlçatan deyil"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Səsli etmək üçün tıklayın."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Vibrasiyanı ayarlamaq üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Səssiz etmək üçün tıklayın. Əlçatımlılıq xidmətləri səssiz edilmiş ola bilər."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Demo rejimini göstərin"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Zəngli saat"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> aktivdir"</string> <string name="wallet_title" msgid="5369767670735827105">"Pulqabı"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Telefonunuzla daha sürətli və təhlükəsiz satınalmalar etmək üçün ayarlayın"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Hamısını göstər"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Taçped jestlərini öyrənin"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura və taçpeddən istifadə edərək hərəkət edin"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Taçped jestləri, klaviatura qısayolları və s. haqqında öyrənin"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Geri qayıdın"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Əsas səhifəyə qayıdın"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Son tətbiqlərə baxın"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hazırdır"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Geri qayıdın"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Taçpeddə üç barmaqla sola və ya sağa sürüşdürün"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Əla!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Geri getmə jestini tamamladınız."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ana ekrana qayıdın"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Taçpeddə üç barmaqla yuxarı sürüşdürün"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Əla!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Əsas səhifəyə keçid jestini tamamladınız"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Son tətbiqlərə baxın"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Taçpeddə üç barmaqla yuxarı çəkib saxlayın"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Əla!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Son tətbiqlərə baxmaq jestini tamamladınız."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Bütün tətbiqlərə baxın"</string> + <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> <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 6064cc10c821..b2a983ebc223 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uklj. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nije podešeno"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte u podešavanjima"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nema aktivnih režima}=1{Aktivan je {mode} režim}one{Aktivan je # režim}few{Aktivna su # režima}other{Aktivno je # režima}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas uznemiravati zvukovi i vibracije osim za alarme, podsetnike, događaje i pozivaoce koje navedete. I dalje ćete čuti sve što odaberete da pustite, uključujući muziku, video snimke i igre."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvuk isključen"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen režim Ne uznemiravaj"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je uključen režim Ne uznemiravaj"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste podesili na vibraciju. Zvuk usluga pristupačnosti će možda biti isključen."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Zvuk usluga pristupačnosti će možda biti isključen."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite pokrete za tačped"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tastature i tačpeda"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučite pokrete za tačped, tasterske prečice i drugo"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Idi na početni ekran"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavno korišćene aplikacije"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Prevucite ulevo ili udesno sa tri prsta na tačpedu"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Svaka čast!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dovršili ste pokret za povratak."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Idi na početni ekran"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prevucite nagore sa tri prsta na tačpedu"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Odlično!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dovršili ste pokret za povratak na početnu stranicu."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaži nedavno korišćene aplikacije"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prevucite nagore i zadržite sa tri prsta na tačpedu"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Odlično!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dovršili ste pokret za prikazivanje nedavno korišćenih aplikacija."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string> + <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> <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 cdaca7efcde0..a0e536b0881f 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Не зададзена"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Адкрыць налады"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Актыўных рэжымаў няма}=1{Рэжым \"{mode}\" актыўны}one{# рэжым актыўны}few{# рэжымы актыўныя}many{# рэжымаў актыўныя}other{# рэжыму актыўныя}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будуць турбаваць гукі і вібрацыя, за выключэннем будзільнікаў, напамінаў, падзей і выбраных вамі абанентаў. Вы будзеце чуць усё, што ўключыце, у тым ліку музыку, відэа і гульні."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недаступна, бо выключаны гук выклікаў"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недаступна, бо ўключаны рэжым \"Не турбаваць\""</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недаступна, бо ўключаны рэжым \"Не турбаваць\""</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дакраніцеся, каб уключыць гук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дакраніцеся, каб уключыць вібрацыю. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дакраніцеся, каб адключыць гук. Можа быць адключаны гук службаў спецыяльных магчымасцей."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Паказваць дэманстрацыйны рэжым"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Будзільнік"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"Уключаны рэжым \"<xliff:g id="MODENAME">%1$s</xliff:g>\""</string> <string name="wallet_title" msgid="5369767670735827105">"Кашалёк"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Наладзьце картку, каб рабіць больш хуткія і бяспечныя куплі з дапамогай тэлефона"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Паказаць усе"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Азнаёмцеся з жэстамі для сэнсарнай панэлі"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навігацыя з дапамогай клавіятуры і сэнсарнай панэлі"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Азнаёмцеся з жэстамі для сэнсарнай панэлі, спалучэннямі клавіш і г. д."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На галоўную старонку"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прагляд нядаўніх праграм"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Гатова"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўлева ці ўправа"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Выдатна!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы навучыліся рабіць жэст вяртання."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На галоўны экран"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўверх"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Выдатная праца!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Вы навучыліся рабіць жэст для пераходу на галоўны экран"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прагляд нядаўніх праграм"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Правядзіце па сэнсарнай панэлі трыма пальцамі ўверх і затрымайце пальцы"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Выдатная праца!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы скончылі вывучэнне жэсту для прагляду нядаўніх праграм."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Глядзець усе праграмы"</string> + <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="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 79fc4b278527..1702b8374b99 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Не е зададено"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управление от настройките"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Няма активни режими}=1{Режимът „{mode}“ е активен}other{# активни режима}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Няма да бъдете обезпокоявани от звуци и вибрирания освен от будилници, напомняния, събития и обаждания от посочени от вас контакти. Пак ще чувате всичко, което изберете да се пусне, включително музика, видеоклипове и игри."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Не е налице, защото звъненето е спряно"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Не е налице, защото режимът „Не безпокойте“ е вкл."</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Не е налице, защото „Не безпокойте“ е вкл."</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Докоснете, за да включите отново звука."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Докоснете, за да зададете вибриране. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Докоснете, за да заглушите звука. Възможно е звукът на услугите за достъпност да бъде заглушен."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научете за жестовете със сензорния панел"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигирайте посредством клавиатурата и сензорния панел"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научете за жестовете със сензорния панел, клавишните комбинации и др."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Към началния екран"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Преглед на скорошните приложения"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Прекарайте три пръста наляво или надясно по сензорния панел"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Чудесно!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Изпълнихте жеста за връщане назад."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Към началния екран"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Прекарайте три пръста нагоре по сензорния панел"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отлично!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Изпълнихте жеста за преминаване към началния екран"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Преглед на скорошните приложения"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Прекарайте три пръста нагоре по сензорния панел и задръжте"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Изпълнихте жеста за преглед на скорошните приложения."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Преглед на всички приложения"</string> + <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="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 485221a76580..a36fe05c263c 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"রিং মিউট করা হয়েছে বলে উপলভ্য নেই"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'বিরক্ত করবে না\' মোড চালু থাকার জন্য উপলভ্য নেই"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\'বিরক্ত করবে না\' মোড চালু থাকার জন্য উপলভ্য নেই"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। সশব্দ করতে আলতো চাপুন।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। কম্পন এ সেট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। মিউট করতে আলতো চাপুন। অ্যাক্সেসযোগ্যতার পরিষেবাগুলিকে মিউট করা হতে পারে।"</string> @@ -1464,7 +1468,7 @@ <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"\'অতিরিক্ত কম ব্রাইটনেস\' ফিচারের শর্টকাট সরানো হয়েছে"</string> <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"কানেক্টিভিটি"</string> <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"অ্যাক্সেসিবিলিটি"</string> - <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"উপযোগিতা"</string> + <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"ইউটিলিটি"</string> <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"গোপনীয়তা"</string> <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"অ্যাপের তরফ থেকে দেওয়া"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"ডিসপ্লে"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 4c79f4879043..13a933e8964a 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uključeno • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nije postavljeno"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte opcijom u postavkama"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nema aktivnih načina rada}=1{Način rada {mode} je aktivan}one{# način rada je aktivan}few{# načina rada su aktivna}other{# načina rada je aktivno}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivalaca koje odredite. I dalje ćete čuti sve što ste odabrali za reprodukciju, uključujući muziku, videozapise i igre."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno zbog isključenog zvona"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je funkcija Ne ometaj uključena"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je funkcija Ne ometaj uključena"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Saznajte više o pokretima na dodirnoj podlozi"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tastature i dodirne podloge"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima na dodirnoj podlozi, prečicama tastature i drugim opcijama"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazad"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Odlazak na početni ekran"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Prikaži nedavne aplikacije"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazad"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Prevucite ulijevo ili udesno s tri prsta na dodirnoj podlozi"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Lijepo!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Savladali ste pokret za vraćanje."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Odlazak na početni ekran"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prevucite nagore s tri prsta na dodirnoj podlozi"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Sjajno!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Savladali ste pokret za otvaranje početnog ekrana"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaz nedavnih aplikacija"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavnih aplikacija."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Pogledajte sve aplikacije"</string> + <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> <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 1552c87fc181..0767849dd775 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activat • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivat"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"No definit"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestiona a la configuració"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No hi ha cap mode actiu}=1{{mode} està actiu}many{Hi ha # de modes actius}other{Hi ha # modes actius}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"No t\'interromprà cap so ni cap vibració, tret dels de les alarmes, recordatoris, esdeveniments i trucades de les persones que especifiquis. Continuaràs sentint tot allò que decideixis reproduir, com ara música, vídeos i jocs."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible perquè el so està silenciat"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible perquè No molestis està activat"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible perquè No molestis està activat"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca per activar el so."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca per activar la vibració. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca per silenciar el so. Pot ser que els serveis d\'accessibilitat se silenciïn."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprèn els gestos del ratolí tàctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega amb el teclat i el ratolí tàctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprèn els gestos del ratolí tàctil, les tecles de drecera i més"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Torna"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pàgina d\'inici"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Llisca cap a l\'esquerra o cap a la dreta amb tres dits al ratolí tàctil"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Molt bé!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Has completat el gest per tornar enrere."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ves a la pantalla d\'inici"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Llisca cap amunt amb tres dits al ratolí tàctil"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Ben fet!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Has completat el gest per anar a la pantalla d\'inici"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Mostra les aplicacions recents"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Llisca cap amunt amb tres dits i mantén premut al ratolí tàctil"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Ben fet!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Has completat el gest per veure les aplicacions recents."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Mostra totes les aplicacions"</string> + <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> <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 16895034b4f5..c7ec42cea19e 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuto"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuto • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuto"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nenastaveno"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Spravovat v nastavení"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žádné aktivní}=1{Režim {mode} je aktivní}few{# režimy jsou aktivní}many{# režimu je aktivních}other{# režimů je aktivních}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudou vás rušit zvuky ani vibrace s výjimkou budíků, upozornění, událostí a volajících, které zadáte. Nadále uslyšíte veškerý obsah, který si sami pustíte (např. hudba, videa nebo hry)."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, protože vyzvánění je ztlumené"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupné, protože je zapnutý režim Nerušit"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupné – je zapnutý režim Nerušit"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnete zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujete režim vibrací. Služby přístupnosti mohou být ztlumeny."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnete zvuk. Služby přístupnosti mohou být ztlumeny."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte se gesta touchpadu"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigujte pomocí klávesnice a touchpadu"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte se gesta touchpadu, klávesové zkratky a další"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zpět"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Přejít na domovskou stránku"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazit nedávné aplikace"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Přejeďte po touchpadu třemi prsty doleva nebo doprava"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Skvělé!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dokončili jste gesto pro přechod zpět."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Přejít na plochu"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Přejeďte po touchpadu třemi prsty nahoru"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Výborně!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dokončili jste gesto pro přechod na plochu"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazit nedávné aplikace"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Přejeďte po touchpadu třemi prsty nahoru a podržte je"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Výborně!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Provedli jste gesto pro zobrazení nedávných aplikací."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazit všechny aplikace"</string> + <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">"Dokončili jste gesto k zobrazení všech aplikací"</string> <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 729473c9450f..b5220d6f526f 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Til"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Til • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Fra"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ikke konfigureret"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrer i indstillingerne"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ingen aktive tilstande}=1{{mode} er aktiv}one{# tilstand er aktiv}other{# tilstande er aktive}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Du bliver ikke forstyrret af lyde eller vibrationer, undtagen fra alarmer, påmindelser, begivenheder og opkald fra udvalgte personer, du selv angiver. Du kan stadig høre alt, du vælger at afspille, f.eks. musik, videoer og spil."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ikke muligt, da ringetonen er slået fra"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ikke tilgængelig, fordi Forstyr ikke er aktiveret"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ikke tilgængelig, fordi Forstyr ikke er aktiveret"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryk for at slå lyden til."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryk for at konfigurere til at vibrere. Tilgængelighedstjenester kan blive deaktiveret."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryk for at slå lyden fra. Lyden i tilgængelighedstjenester kan blive slået fra."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Se bevægelser på touchpladen"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviger ved hjælp af dit tastatur og din touchplade"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Se bevægelser på touchpladen, tastaturgenveje m.m."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbage"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se seneste apps"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Udfør"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Stryg til venstre eller højre med tre fingre på touchpladen"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sådan!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har fuldført bevægelsen for Gå tilbage."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gå til startskærmen"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Stryg opad med tre fingre på touchpladen"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Flot!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du har udført bevægelsen for at gå til startsiden"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se seneste apps"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Stryg opad, og hold tre fingre på touchpladen"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Godt klaret!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har udført bevægelsen for at se de seneste apps."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apps"</string> + <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> <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 655b7a2345ca..9367d40fd67e 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nicht verfügbar, da Klingelton stumm"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nicht verfügbar, weil „Bitte nicht stören“ an ist"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nicht verfügbar, weil „Bitte nicht stören“ an"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Zum Aufheben der Stummschaltung tippen."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tippen, um Vibrieren festzulegen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Zum Stummschalten tippen. Bedienungshilfen werden unter Umständen stummgeschaltet."</string> @@ -706,8 +710,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Demomodus anzeigen"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Weckruf"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"„<xliff:g id="MODENAME">%1$s</xliff:g>“ ist aktiviert"</string> <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Füge eine Zahlungsmethode hinzu, um noch schneller und sicherer mit deinem Smartphone zu bezahlen"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Alle anzeigen"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 3c99fbb3ce09..18b99aae6723 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Δεν έχει οριστεί"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Διαχείριση στις ρυθμίσεις"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Δεν υπάρχει ενεργή λειτουργία}=1{{mode} ενεργή λειτουργία}other{# ενεργές λειτουργίες}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Δεν θα ενοχλείστε από ήχους και δονήσεις, παρά μόνο από ξυπνητήρια, υπενθυμίσεις, συμβάντα και καλούντες που έχετε καθορίσει. Θα εξακολουθείτε να ακούτε όλο το περιεχόμενο που επιλέγετε να αναπαραγάγετε, συμπεριλαμβανομένης της μουσικής, των βίντεο και των παιχνιδιών."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Μη διαθέσιμο λόγω σίγασης ήχου κλήσης"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Μη διαθ. επειδή η λειτ. Μην ενοχλείτε είναι ενεργή"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Μη διαθ. επειδή η λειτ. Μην ενοχλείτε είναι ενεργή"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Πατήστε για κατάργηση σίγασης."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Πατήστε για ενεργοποιήσετε τη δόνηση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Πατήστε για σίγαση. Οι υπηρεσίες προσβασιμότητας ενδέχεται να τεθούν σε σίγαση."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Μάθετε κινήσεις επιφάνειας αφής"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Πλοήγηση με το πληκτρολόγιο και την επιφάνεια αφής"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Μάθετε κινήσεις επιφάνειας αφής, συντομεύσεις πληκτρολογίου και άλλα"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Επιστροφή"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Αρχική"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Προβολή πρόσφατων εφαρμογών"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Τέλος"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Επιστροφή"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Σύρετε προς τα αριστερά ή τα δεξιά με τρία δάχτυλα στην επιφάνεια αφής"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Ωραία!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ολοκληρώσατε την κίνηση επιστροφής."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Αρχική"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Σύρετε προς τα επάνω με τρία δάχτυλα στην επιφάνεια αφής"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Μπράβο!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ολοκληρώσατε την κίνηση μετάβασης στην αρχική οθόνη"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Προβολή πρόσφατων εφαρμογών"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Σύρετε προς τα επάνω με τρία δάχτυλα στην επιφάνεια αφής και μην τα σηκώσετε"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Μπράβο!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ολοκληρώσατε την κίνηση για την προβολή πρόσφατων εφαρμογών."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Προβολή όλων των εφαρμογών"</string> + <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="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 4570c0a35eff..ba945f229051 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Not set"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe left or right using three fingers on your touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Nice!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"You completed the go back gesture."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Go home"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe up with three fingers on your touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Well done!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"You completed the go home gesture"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe up and hold using three fingers on your touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string> + <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="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 3d33fa0b8400..a26d1611f825 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 4570c0a35eff..ba945f229051 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Not set"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe left or right using three fingers on your touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Nice!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"You completed the go back gesture."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Go home"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe up with three fingers on your touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Well done!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"You completed the go home gesture"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe up and hold using three fingers on your touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string> + <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="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 4570c0a35eff..ba945f229051 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"On"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"On • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Off"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Not set"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Manage in settings"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{No active modes}=1{{mode} is active}other{# modes are active}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"You won\'t be disturbed by sounds and vibrations, except from alarms, reminders, events and callers you specify. You\'ll still hear anything you choose to play including music, videos and games."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Learn touchpad gestures"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigate using your keyboard and touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Learn touchpad gestures, keyboards shortcuts and more"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Go back"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Go home"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"View recent apps"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Done"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Go back"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe left or right using three fingers on your touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Nice!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"You completed the go back gesture."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Go home"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe up with three fingers on your touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Well done!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"You completed the go home gesture"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"View recent apps"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe up and hold using three fingers on your touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Well done!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"You completed the view recent apps gesture."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"View all apps"</string> + <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="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-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 3d157c7b7648..b70fdebe8302 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Unavailable because ring is muted"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Unavailable because Do Not Disturb is on"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Unavailable because Do Not Disturb is on"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tap to unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tap to set to vibrate. Accessibility services may be muted."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tap to mute. Accessibility services may be muted."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 6c80a2c81954..0620cc3fa068 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible por timbre silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible si está activado No interrumpir"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible si está activado No interrumpir"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Presiona para dejar de silenciar."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Presiona para establecer el modo vibración. Es posible que los servicios de accesibilidad estén silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Presiona para silenciar. Es posible que los servicios de accesibilidad estén silenciados."</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index f77119e3b90a..c10fc7ad17bf 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"No disponible (el tono está silenciado)"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"No disponible porque No molestar está activado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"No disponible porque No molestar está activado"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar el sonido."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para poner el dispositivo en vibración. Los servicios de accesibilidad pueden silenciarse."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Los servicios de accesibilidad pueden silenciarse."</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 3323a3f157db..4b97b7be2263 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Sees"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Sees • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Väljas"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Määramata"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Seadetes halamine"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Aktiivsed režiimid puuduvad}=1{{mode} on aktiivne}other{# režiimi on aktiivsed}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Helid ja värinad ei sega teid. Kuulete siiski enda määratud äratusi, meeldetuletusi, sündmusi ja helistajaid. Samuti kuulete kõike, mille esitamise ise valite, sh muusika, videod ja mängud."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Pole saadaval, kuna helin on vaigistatud"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Pole saadaval, kuna režiim Mitte segada on sees"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Pole saadaval, kuna režiim Mitte segada on sees"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Puudutage vaigistuse tühistamiseks."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Puudutage värinarežiimi määramiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Puudutage vaigistamiseks. Juurdepääsetavuse teenused võidakse vaigistada."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Õppige puuteplaadi liigutusi"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeerige klaviatuuri ja puuteplaadi abil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Õppige puuteplaadi liigutusi, klaviatuuri otseteid ja palju muud"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Mine tagasi"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Avalehele"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Hiljutiste rakenduste vaatamine"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Tagasi"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pühkige puuteplaadil kolme sõrmega vasakule või paremale"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Tubli töö!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Tegite tagasiliikumise liigutuse."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Avalehele"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pühkige puuteplaadil kolme sõrmega üles"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Väga hea!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Tegite avakuvale minemise liigutuse"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Hiljutiste rakenduste vaatamine"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pühkige üles ja hoidke kolme sõrme puuteplaadil"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Väga hea!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Lõpetasite hiljutiste rakenduste vaatamise liigutuse."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Kõigi rakenduste kuvamine"</string> + <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> <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 246c23085159..eff98ce47e66 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Aktibatuta"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktibo • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desaktibatuta"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ezarri gabe"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kudeatu ezarpenetan"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ez dago modurik aktibo}=1{\"{mode}\" aktibo dago}other{# modu aktibo daude}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Gailuak ez du egingo ez soinurik ez dardararik, baina alarmak, gertaera eta abisuen tonuak, eta aukeratzen dituzun deitzaileen dei-tonuak joko ditu. Bestalde, zuk erreproduzitutako guztia entzungo duzu, besteak beste, musika, bideoak eta jokoak."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ez dago erabilgarri, tonua desaktibatuta dagoelako"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ez dago erabilgarri, ez molestatzeko modua aktibatuta baitago"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ez dago erabilgarri, ez molestatzeko modua aktibatuta baitago"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sakatu audioa aktibatzeko."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Sakatu dardara ezartzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sakatu audioa desaktibatzeko. Baliteke erabilerraztasun-eginbideen audioa desaktibatzea."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Erakutsi demo modua"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarma"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> aktibatuta dago"</string> <string name="wallet_title" msgid="5369767670735827105">"Diru-zorroa"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Konfiguratu erosketa bizkorrago eta seguruagoak egiteko telefonoarekin"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Erakutsi guztiak"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Ikasi ukipen-paneleko keinuak"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Nabigatu teklatua eta ukipen-panela erabilita"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Ikasi ukipen-paneleko keinuak, lasterbideak eta abar"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Egin atzera"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Joan orri nagusira"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ikusi azkenaldiko aplikazioak"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Eginda"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Egin atzera"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pasatu 3 hatz ezkerrera edo eskuinera ukipen-panelean"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Ederki!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ikasi duzu atzera egiteko keinua."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Joan orri nagusira"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pasatu 3 hatz gora ukipen-panelean"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bikain!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ikasi duzu orri nagusira joateko keinua"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ikusi azkenaldiko aplikazioak"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pasatu 3 hatz gora eta eduki sakatuta ukipen-panelean"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bikain!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Osatu duzu azkenaldiko aplikazioak ikusteko keinua."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ikusi aplikazio guztiak"</string> + <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> <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 29a2739dd74f..858670affe36 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"تنظیم نشده است"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"مدیریت در تنظیمات"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{حالت فعالی وجود ندارد}=1{{mode} فعال است}one{# حالت فعال است}other{# حالت فعال است}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"بهجز هشدارها، یادآوریها، رویدادها و تماسگیرندگانی که خودتان مشخص میکنید، هیچ صدا و لرزشی نخواهید داشت. همچنان صدای مواردی را که پخش میکنید میشنوید (ازجمله صدای موسیقی، ویدیو و بازی)."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"دردسترس نیست، چون زنگ بیصدا شده است"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"دردسترس نیست زیرا «مزاحم نشوید» روشن است"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. برای باصدا کردن تکضرب بزنید."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. برای تنظیم روی لرزش تکضرب بزنید. ممکن است سرویسهای دسترسپذیری بیصدا شوند."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. برای صامت کردن تکضرب بزنید. ممکن است سرویسهای دسترسپذیری صامت شود."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"آشنایی با اشارههای صفحه لمسی"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"پیمایش کردن بااستفاده از صفحهکلید و صفحه لمسی"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"آشنایی با اشارههای صفحه لمسی، میانبرهای صفحهکلید، و موارد دیگر"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"برگشتن"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"رفتن به صفحه اصلی"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"مشاهده کردن برنامههای اخیر"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"تمام"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"برگشتن"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"با سه انگشت روی صفحه لمسی تند به چپ یا راست بکشید."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"چه خوب!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"اشاره برگشت را تکمیل کردید."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"رفتن به صفحه اصلی"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"با سه انگشت روی صفحه لمسی تند به بالا بکشید"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"عالی است!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"اشاره رفتن به صفحه اصلی را تکمیل کردید"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"مشاهده کردن برنامههای اخیر"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"با سه انگشت روی صفحه لمسی تند به بالا بکشید و نگه دارید"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"عالی است!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"اشاره مشاهده برنامههای اخیر را انجام دادید"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"مشاهده همه برنامهها"</string> + <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="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 e93ac1a51f72..96a701af9f42 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Päällä"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Päällä • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Pois päältä"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ei asetettu"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Muuta asetuksista"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ei aktiivisia tiloja}=1{{mode} on aktiivinen}other{# tilaa on aktiivisena}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Äänet ja värinät eivät häiritse sinua, paitsi jos ne ovat hälytyksiä, muistutuksia, tapahtumia tai määrittämiäsi soittajia. Kuulet edelleen kaiken valitsemasi sisällön, kuten musiikin, videot ja pelit."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ei käytettävissä, soittoääni mykistetty"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ei saatavilla, koska Älä häiritse on päällä"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ei saatavilla, koska Älä häiritse on päällä"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Poista mykistys koskettamalla."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Siirry värinätilaan koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Mykistä koskettamalla. Myös esteettömyyspalvelut saattavat mykistyä."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Opettele kosketuslevyn eleitä"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Siirry käyttämällä näppäimistöä ja kosketuslevyä"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Opettele kosketuslevyn eleitä, pikanäppäimiä ja muuta"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Takaisin"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Siirry etusivulle"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Katso viimeisimmät sovellukset"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Valmis"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Takaisin"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pyyhkäise kosketuslevyllä vasemmalle tai oikealle kolmella sormella"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Hienoa!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Olet oppinut Takaisin-eleen."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Siirry etusivulle"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pyyhkäise ylös kolmella sormella kosketuslevyllä"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Hienoa!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Olet oppinut aloitusnäytölle palaamiseleen"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Katso viimeisimmät sovellukset"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Hienoa!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Olet oppinut Katso viimeisimmät sovellukset ‑eleen."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Näytä kaikki sovellukset"</string> + <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> <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 c58ad93b8f07..6b718085398b 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Inaccessible : sonnerie en sourdine"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inaccessible parce que Ne pas déranger est activée"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Inaccessible parce que Ne pas déranger est activée"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Touchez pour réactiver le son."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Touchez pour activer les vibrations. Il est possible de couper le son des services d\'accessibilité."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Touchez pour couper le son. Il est possible de couper le son des services d\'accessibilité."</string> @@ -706,8 +710,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Afficher le mode Démo"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"Le mode <xliff:g id="MODENAME">%1$s</xliff:g> est activé"</string> <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Préparez-vous à faire des achats plus rapidement et de façon plus sûre avec votre téléphone"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Tout afficher"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index cf1cae9dad04..dccb581d88bf 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -579,7 +579,7 @@ <string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string> <string name="adaptive_notification_edu_hun_title" msgid="7790738150177329960">"La limitation des notifications est activée"</string> - <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes max. quand vous recevez trop de notifications à la fois."</string> + <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes maximum quand vous recevez trop de notifications à la fois."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverrouiller pour voir anciennes notifications"</string> <string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Cet appareil est géré par tes parents"</string> @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponible, car la sonnerie est coupée"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponible car Ne pas déranger est activé"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponible car Ne pas déranger est activé"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Appuyez pour ne plus ignorer."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Appuyez pour mettre en mode vibreur. Vous pouvez ignorer les services d\'accessibilité."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Appuyez pour ignorer. Vous pouvez ignorer les services d\'accessibilité."</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index dc0f216ef4db..b0a432d9ee93 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activado"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activo • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desactivado"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Sen configurar"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Xestionar na configuración"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Non hai ningún modo activo}=1{{mode} está activo}other{Hai # modos activos}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Non te molestará ningún son nin vibración, agás os procedentes de alarmas, recordatorios, eventos e os emisores de chamada especificados. Seguirás escoitando todo aquilo que decidas reproducir, mesmo a música, os vídeos e os xogos."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non dispoñible: o son está silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non dispoñible: Non molestar está activado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Non dispoñible: Non molestar está activado"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toca para activar o son."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toca para establecer a vibración. Pódense silenciar os servizos de accesibilidade."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toca para silenciar. Pódense silenciar os servizos de accesibilidade."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprende a usar os xestos do panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega co teclado e o panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende a usar os xestos do panel táctil, atallos de teclado e moito máis"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Volver"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir ao inicio"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Consultar aplicacións recentes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Feito"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pasa tres dedos cara á esquerda ou cara á dereita no panel táctil"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Excelente!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaches o xesto de retroceso."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir ao inicio"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pasa tres dedos cara arriba no panel táctil"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelente traballo."</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaches o titorial do xesto de ir ao inicio"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Consultar aplicacións recentes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pasa tres dedos cara arriba e mantenos premidos no panel táctil"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Moi ben!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaches o titorial do xesto de consultar aplicacións recentes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as aplicacións"</string> + <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> <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 866535e20489..5de229333397 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"સેટઅપ કર્યું નથી"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"સેટિંગમાં જઈને મેનેજ કરો"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{કોઈ સક્રિય મોડ નથી}=1{{mode} સક્રિય છે}one{# મોડ સક્રિય છે}other{# મોડ સક્રિય છે}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"અલાર્મ, રિમાઇન્ડર, ઇવેન્ટ અને તમે ઉલ્લેખ કરો તે કૉલર સિવાય તમને ધ્વનિ કે વાઇબ્રેશન દ્વારા ખલેલ પહોંચાડવામાં આવશે નહીં. સંગીત, વીડિઓ અને રમતો સહિત તમે જે કંઈપણ ચલાવવાનું પસંદ કરશો તે સંભળાતું રહેશે."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"રિંગ મ્યૂટ કરી હોવાના કારણે અનુપલબ્ધ છે"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ઉપલબ્ધ નથી, કારણ કે ખલેલ પાડશો નહીં મોડ ચાલુ છે"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ઉપલબ્ધ નથી, કારણ કે ખલેલ પાડશો નહીં મોડ ચાલુ છે"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. અનમ્યૂટ કરવા માટે ટૅપ કરો."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. વાઇબ્રેટ પર સેટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. મ્યૂટ કરવા માટે ટૅપ કરો. ઍક્સેસિબિલિટી સેવાઓ મ્યૂટ કરવામાં આવી શકે છે."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ટચપૅડના સંકેતો વિશે જાણો"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"તમારા કીબોર્ડ અને ટચપૅડ વડે નૅવિગેટ કરો"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ટચપૅડના સંકેતો અને કીબોર્ડના શૉર્ટકટ જેવું બીજું ઘણું જાણો"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"પાછા જાઓ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"હોમ પર જાઓ"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"તાજેતરની ઍપ જુઓ"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"થઈ ગયું"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"પાછા જાઓ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"તમારા ટચપૅડ પર ત્રણ આંગળીનો ઉપયોગ કરીને ડાબે કે જમણે સ્વાઇપ કરો"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"સરસ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"તમે પાછા જવાનો સંકેત પૂર્ણ કર્યો છે."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"હોમ પર જાઓ"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"તમારા ટચપૅડ પર ત્રણ આંગળી વડે ઉપરની તરફ સ્વાઇપ કરો"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ખૂબ સરસ કામ!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"તમે હોમ સ્ક્રીન પર જવાનો સંકેત પૂર્ણ કર્યો"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"તાજેતરની ઍપ જુઓ"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"તમારા ટચપૅડ પર ત્રણ આંગળીઓનો ઉપયોગ કરીને ઉપર સ્વાઇપ કરો અને દબાવી રાખો"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ખૂબ સરસ કામ!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"તમે \"તાજેતરની ઍપ જુઓ\" સંકેત પૂર્ણ કર્યો."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"બધી ઍપ જુઓ"</string> + <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="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 7a1bf83a101e..15b363962859 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट नहीं है"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग में जाकर मैनेज करें"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कोई मोड चालू नहीं है}=1{{mode} चालू है}one{# मोड चालू है}other{# मोड चालू हैं}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"आपको अलार्म, रिमाइंडर, इवेंट और चुनिंदा कॉल करने वालों के अलावा किसी और तरह से (आवाज़ करके और थरथरा कर ) परेशान नहीं किया जाएगा. आप फिर भी संगीत, वीडियो और गेम सहित अपना चुना हुआ सब कुछ सुन सकते हैं."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"आवाज़ नहीं आएगी, क्योंकि रिंग म्यूट है"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"सुविधा बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"आवाज़ बंद है, क्योंकि \'परेशान न करें\' मोड चालू है"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करने के लिए टैप करें."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. कंपन पर सेट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करने के लिए टैप करें. सुलभता सेवाएं म्यूट हो सकती हैं."</string> @@ -1317,7 +1320,7 @@ <string name="log_access_confirmation_title" msgid="4843557604739943395">"<xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> को डिवाइस लॉग ऐक्सेस करने की अनुमति देनी है?"</string> <string name="log_access_confirmation_allow" msgid="752147861593202968">"एक बार ऐक्सेस करने की अनुमति दें"</string> <string name="log_access_confirmation_deny" msgid="2389461495803585795">"अनुमति न दें"</string> - <string name="log_access_confirmation_body" msgid="6883031912003112634">"डिवाइस लॉग में आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए कर सकते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर मौजूद अपने लॉग ऐक्सेस कर सकता है. डिवाइस को बनाने वाली कंपनी फिर भी डिवाइस के कुछ लॉग या जानकारी ऐक्सेस कर सकती है."</string> + <string name="log_access_confirmation_body" msgid="6883031912003112634">"डिवाइस लॉग में आपके डिवाइस पर की गई कार्रवाइयां रिकॉर्ड होती हैं. ऐप्लिकेशन, इन लॉग का इस्तेमाल गड़बड़ियां ढूंढने और उन्हें ठीक करने के लिए कर सकते हैं.\n\nकुछ लॉग में संवेदनशील जानकारी हो सकती है. इसलिए, सिर्फ़ भरोसेमंद ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस दें. \n\nअगर इस ऐप्लिकेशन को डिवाइस के सभी लॉग का ऐक्सेस नहीं दिया जाता है, तब भी यह डिवाइस पर मौजूद अपने लॉग ऐक्सेस कर सकता है. इसके अलावा, डिवाइस को बनाने वाली कंपनी भी डिवाइस के कुछ लॉग या जानकारी ऐक्सेस कर सकती है."</string> <string name="log_access_confirmation_learn_more" msgid="3134565480986328004">"ज़्यादा जानें"</string> <string name="log_access_confirmation_learn_more_at" msgid="5635666259505215905">"ज़्यादा जानने के लिए <xliff:g id="URL">%s</xliff:g> पर जाएं"</string> <string name="keyguard_affordance_enablement_dialog_action_template" msgid="8164857863036314664">"<xliff:g id="APPNAME">%1$s</xliff:g> खोलें"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपैड पर हाथ के जेस्चर के बारे में जानें"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"कीबोर्ड और टचपैड का इस्तेमाल करके नेविगेट करें"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपैड पर हाथ के जेस्चर, कीबोर्ड शॉर्टकट वगैरह के बारे में जानें"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"वापस जाएं"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रीन पर जाएं"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"हो गया"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"वापस जाएं"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"अपने टचपैड पर तीन उंगलियों का इस्तेमाल करके, बाईं या दाईं ओर स्वाइप करें"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"बढ़िया!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"अब आपने जान लिया है कि हाथ का जेस्चर इस्तेमाल करके, पिछली स्क्रीन पर वापस कैसे जाएं."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होम स्क्रीन पर जाएं"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"बहुत बढ़िया!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"अब आपको इस बारे में जानकारी है कि हाथ के जेस्चर का इस्तेमाल करके होम स्क्रीन पर कैसे जाते हैं"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और फिर होल्ड करें"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"बहुत बढ़िया!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"आपने हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखने के लिए, हाथ के जेस्चर के बारे में जान लिया है."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"सभी ऐप्लिकेशन देखें"</string> + <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="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 96071202926e..f646ee613611 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Uključeno"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Uklj. • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Isključeno"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nije postavljeno"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Upravljajte u postavkama"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nema aktivnih načina}=1{Aktivno: {mode}}one{# način je aktivan}few{# načina su aktivna}other{# načina je aktivno}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivatelja koje navedete. I dalje ćete čuti sve što želite reproducirati, uključujući glazbu, videozapise i igre."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupno jer je zvono utišano"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupno jer je uključen način Ne uznemiravaj"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupno jer je uključen način Ne uznemiravaj"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da biste uključili zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite da biste postavili na vibraciju. Usluge pristupačnosti možda neće imati zvuk."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da biste isključili zvuk. Usluge pristupačnosti možda neće imati zvuk."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Saznajte više o pokretima za dodirnu podlogu"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krećite se pomoću tipkovnice i dodirne podloge"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Saznajte više o pokretima za dodirnu podlogu, tipkovnim prečacima i ostalom"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Natrag"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Na početni zaslon"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Pregled nedavnih aplikacija"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotovo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Natrag"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Prijeđite ulijevo ili udesno trima prstima na dodirnoj podlozi"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Odlično!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Izvršili ste pokret za povratak."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Na početnu stranicu"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prijeđite prema gore trima prstima na dodirnoj podlozi"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Sjajno!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Izvršili ste pokret za otvaranje početnog zaslona"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Pregled nedavnih aplikacija"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prijeđite prema gore trima prstima na dodirnoj podlozi i zadržite pritisak"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Izvršili ste pokret za prikaz nedavno korištenih aplikacija."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Prikaži sve aplikacije"</string> + <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">"Izvršili ste pokret za prikaz svih aplikacija"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 3a9735bbf7fd..1248f41aa8ae 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Be"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Be • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Ki"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nincs beállítva"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"A Beállítások között kezelheti"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nincs aktív mód}=1{A(z) {mode} aktív}other{# mód aktív}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Az Ön által meghatározott ébresztéseken, emlékeztetőkön, eseményeken és hívókon kívül nem fogja Önt más hang vagy rezgés megzavarni. Továbbra is lesz hangjuk azoknak a tartalmaknak, amelyeket Ön elindít, például zenék, videók és játékok."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nem lehetséges, a csörgés le van némítva"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nem működik, mert be van kapcsolva a Ne zavarjanak"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Koppintson a némítás megszüntetéséhez."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Koppintson a rezgés beállításához. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Koppintson a némításhoz. Előfordulhat, hogy a kisegítő lehetőségek szolgáltatásai le vannak némítva."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Érintőpad-kézmozdulatok megismerése"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigálás a billentyűzet és az érintőpad használatával"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Érintőpad-kézmozdulatok, billentyűparancsok és egyebek megismerése"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Vissza"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ugrás a főoldalra"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Legutóbbi alkalmazások megtekintése"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kész"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Vissza"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Csúsztassa gyorsan három ujját balra vagy jobbra az érintőpadon."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Remek!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Teljesítette a visszalépési kézmozdulatot."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ugrás a főoldalra"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Csúsztasson gyorsan felfelé három ujjával az érintőpadon."</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Kiváló!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Teljesítette a kezdőképernyőre lépés kézmozdulatát."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Legutóbbi alkalmazások megtekintése"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Csúsztasson gyorsan felfelé három ujjal az érintőpadon, és tartsa rajta lenyomva az ujjait."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kiváló!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Teljesítette a legutóbbi alkalmazások megtekintésének kézmozdulatát."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Összes alkalmazás megtekintése"</string> + <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> <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 804550cb6319..db58b355f6d2 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Հասանելի չէ, երբ զանգի ձայնն անջատված է"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Հասանելի չէ․ «Չանհանգստացնել» ռեժիմը միացված է"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s: Հպեք՝ ձայնը միացնելու համար:"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s: Հպեք՝ թրթռոցը միացնելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s: Հպեք՝ ձայնն անջատելու համար: Մատչելիության ծառայությունների ձայնը կարող է անջատվել:"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 98bbf4f8ca5d..2c2927553ea0 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -98,7 +98,7 @@ <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"Batas kiri <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string> <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"Batas kanan <xliff:g id="PERCENT">%1$d</xliff:g>%%"</string> <string name="screenshot_work_profile_notification" msgid="203041724052970693">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil kerja"</string> - <string name="screenshot_private_profile_notification" msgid="1704440899154243171">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil pribadi"</string> + <string name="screenshot_private_profile_notification" msgid="1704440899154243171">"Disimpan di <xliff:g id="APP">%1$s</xliff:g> di profil privasi"</string> <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"File"</string> <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> mendeteksi screenshot ini."</string> <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> dan aplikasi terbuka lainnya mendeteksi screenshot ini."</string> @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Aktif"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aktif • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Nonaktif"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Tidak disetel"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Kelola di setelan"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Tidak ada mode yang aktif}=1{{mode} aktif}other{# mode aktif}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Anda tidak akan terganggu oleh suara dan getaran, kecuali dari alarm, pengingat, acara, dan penelepon yang Anda tentukan. Anda akan tetap mendengar apa pun yang telah dipilih untuk diputar, termasuk musik, video, dan game."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia - Volume dering dibisukan"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia - Fitur Jangan Ganggu aktif"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketuk untuk menyuarakan."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketuk untuk menyetel agar bergetar. Layanan aksesibilitas mungkin dibisukan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketuk untuk membisukan. Layanan aksesibilitas mungkin dibisukan."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Pelajari gestur touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Menavigasi menggunakan keyboard dan touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Pelajari gestur touchpad, pintasan keyboard, dan lainnya"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Kembali"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Buka layar utama"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Lihat aplikasi terbaru"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Selesai"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Kembali"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Geser ke kiri atau kanan menggunakan tiga jari di touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bagus!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Anda telah menyelesaikan gestur kembali."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Buka layar utama"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Geser ke atas dengan tiga jari di touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bagus!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Anda telah menyelesaikan gestur buka layar utama"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Lihat aplikasi terbaru"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Geser ke atas dan tahan menggunakan tiga jari di touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bagus!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Anda telah menyelesaikan gestur untuk melihat aplikasi terbaru."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Lihat semua aplikasi"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Tekan tombol tindakan di keyboard"</string> + <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Bagus!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Anda telah menyelesaikan gestur untuk melihat semua aplikasi"</string> <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 b12a475b9efd..1897ba414d57 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Kveikt"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Kveikt • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Slökkt"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ekki stillt"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Stjórna í stillingum"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Engar virkar stillingar}=1{{mode} er virk}one{# stilling er virk}other{# stillingar eru virkar}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Þú verður ekki fyrir truflunum frá hljóðmerkjum og titringi, fyrir utan vekjara, áminningar, viðburði og símtöl frá þeim sem þú leyfir fyrirfram. Þú heyrir áfram í öllu sem þú velur að spila, svo sem tónlist, myndskeiðum og leikjum."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ekki í boði þar sem hringing er þögguð"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ekki í boði því að kveikt er á „Ónáðið ekki“"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ekki í boði því að kveikt er á „Ónáðið ekki“"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ýttu til að hætta að þagga."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ýttu til að stilla á titring. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ýttu til að þagga. Hugsanlega verður slökkt á hljóði aðgengisþjónustu."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Nánar um bendingar á snertifleti"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Flettu með því að nota lyklaborðið og snertiflötinn"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Kynntu þér bendingar á snertifleti, flýtilykla og fleira"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Til baka"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Fara á heimaskjá"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Sjá nýleg forrit"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Lokið"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Til baka"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Strjúktu til hægri eða vinstri á snertifletinum með þremur fingrum"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Flott!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Þú laukst við að kynna þér bendinguna „til baka“."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Heim"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Strjúktu upp á snertifletinum með þremur fingrum"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Vel gert!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Þú framkvæmdir bendinguna „Fara á heimaskjá“"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Sjá nýleg forrit"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Strjúktu upp og haltu þremur fingrum inni á snertifletinum."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Vel gert!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Þú framkvæmdir bendinguna til að sjá nýleg forrit."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Sjá öll forrit"</string> + <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> <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 4fdf01d7622f..2931b96f0888 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Non disponibile con l\'audio disattivato"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Non disponibile: modalità Non disturbare attiva"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Non disponibili con \"Non disturbare\" attiva"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tocca per riattivare l\'audio."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tocca per attivare la vibrazione. L\'audio dei servizi di accessibilità può essere disattivato."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tocca per disattivare l\'audio. L\'audio dei servizi di accessibilità può essere disattivato."</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 78d60e575499..72fc195b056f 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"לא הוגדר"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"שינוי ב\'הגדרות\'"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{אין מצבים פעילים}=1{מצב פעיל אחד ({mode})}one{# מצבים פעילים}two{# מצבים פעילים}other{# מצבים פעילים}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"כדי לא להפריע לך, המכשיר לא ירטוט ולא ישמיע שום צליל, חוץ מהתראות, תזכורות, אירועים ושיחות ממתקשרים מסוימים לבחירתך. המצב הזה לא ישפיע על צלילים שהם חלק מתוכן שבחרת להפעיל, כמו מוזיקה, סרטונים ומשחקים."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"לא זמין כי הצלצול מושתק"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"לא זמין כי התכונה \'נא לא להפריע\' מופעלת"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. יש להקיש כדי לבטל את ההשתקה."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. צריך להקיש כדי להגדיר רטט. ייתכן ששירותי הנגישות מושתקים."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. יש להקיש כדי להשתיק. ייתכן ששירותי הנגישות יושתקו."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"מידע על התנועות בלוח המגע"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ניווט באמצעות המקלדת ולוח המגע"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"מידע על התנועות בלוח המגע, מקשי קיצור ועוד"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"חזרה"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"חזרה לדף הבית"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"הצגת האפליקציות האחרונות"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"סיום"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"חזרה"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"מחליקים שמאלה או ימינה עם שלוש אצבעות על לוח המגע"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"איזה יופי!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"השלמת את התנועה \'הקודם\'."</string> - <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר לדף הבית"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר למסך הבית"</string> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"מחליקים כלפי מעלה עם שלוש אצבעות על לוח המגע"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"מעולה!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"השלמת את תנועת החזרה למסך הבית"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"הצגת האפליקציות האחרונות"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"השלמת את התנועה להצגת האפליקציות האחרונות."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"הצגת כל האפליקציות"</string> + <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="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 2a35e17cd259..dcbfc8ce69be 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"着信音がミュートされているため利用できません"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"サイレント モードが ON のため利用できません"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"サイレント モードが ON のため利用できません"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。タップしてミュートを解除します。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。タップしてバイブレーションに設定します。ユーザー補助機能サービスがミュートされる場合があります。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。タップしてミュートします。ユーザー補助機能サービスがミュートされる場合があります。"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 05a7e42a5937..65d85756c4d9 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ზარის დადუმების გამო ხელმისაწვდომი არაა"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"მიუწვდომელია, ჩართულია „არ შემაწუხოთ“ რეჟიმი"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"მიუწვდომელია, ჩართულია „არ შემაწუხოთ“ რეჟიმი"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. შეეხეთ დადუმების გასაუქმებლად."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. შეეხეთ ვიბრაციაზე დასაყენებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. შეეხეთ დასადუმებლად. შეიძლება დადუმდეს მარტივი წვდომის სერვისებიც."</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 328c82fba67f..7478e7631bb8 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Орнатылмаған"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"\"Параметрлер\" бөлімінде реттеу"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Қосулы режим жоқ}=1{{mode} қосулы}other{# режим қосулы}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Оятқыш, еске салғыш, іс-шара мен өзіңіз көрсеткен контактілердің қоңырауларынан басқа дыбыстар мен дірілдер мазаламайтын болады. Музыка, бейне және ойын сияқты медиафайлдарды қоссаңыз, оларды естисіз."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Қолжетімді емес, шылдырлату өшірулі."</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Мазаламау режимі қосылғандықтан өзгерту мүмкін емес."</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дыбысын қосу үшін түртіңіз."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Діріл режимін орнату үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дыбысын өшіру үшін түртіңіз. Арнайы мүмкіндік қызметтерінің дыбысы өшуі мүмкін."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсорлық тақта қимылдарын үйреніңіз."</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Пернетақтамен және сенсорлық тақтамен жұмыс істеңіз"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсорлық тақта қимылдарын, перне тіркесімдерін және т.б. үйреніңіз."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артқа"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Негізгі бетке өту"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Соңғы қолданбаларды көру"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Дайын"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артқа"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Сенсорлық тақтада үш саусақпен оңға немесе солға сырғытыңыз."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Керемет!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Артқа қайту қимылын аяқтадыңыз."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Негізгі экранға өту"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Сенсорлық тақтада үш саусақпен жоғары сырғытыңыз."</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Жарайсыз!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Негізгі экранға қайту қимылын орындадыңыз."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Соңғы қолданбаларды көру"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Сенсорлық тақтада үш саусақпен жоғары сырғытып, басып тұрыңыз."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Жарайсыз!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Соңғы қолданбаларды көру қимылын орындадыңыз."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Барлық қолданбаны көру"</string> + <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="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 dce1adc32540..d0f3f848fca9 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"មិនបានកំណត់"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"គ្រប់គ្រងនៅក្នុងការកំណត់"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{គ្មានមុខងារដែលកំពុងដំណើរការទេ}=1{{mode} កំពុងដំណើរការ}other{មុខងារ # កំពុងដំណើរការ}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"សំឡេង និងរំញ័រនឹងមិនរំខានដល់អ្នកឡើយ លើកលែងតែម៉ោងរោទ៍ ការរំលឹក ព្រឹត្តិការណ៍ និងអ្នកហៅទូរសព្ទដែលអ្នកបញ្ជាក់ប៉ុណ្ណោះ។ អ្នកនឹងនៅតែឮសំឡេងសកម្មភាពគ្រប់យ៉ាងដែលអ្នកលេងដដែល រួមទាំងតន្រ្តី វីដេអូ និងហ្គេម។"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"មិនអាចប្រើបានទេ ព្រោះសំឡេងរោទ៍ត្រូវបានបិទ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"មិនអាចប្រើបានទេ ដោយសារមុខងារកុំរំខានត្រូវបានបើក"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"មិនអាចប្រើបានទេ ដោយសារមុខងារកុំរំខានត្រូវបានបើក"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s។ ប៉ះដើម្បីបើកសំឡេង។"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s។ ប៉ះដើម្បីកំណត់ឲ្យញ័រ។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s។ ប៉ះដើម្បីបិទសំឡេង។ សេវាកម្មលទ្ធភាពប្រើប្រាស់អាចនឹងត្រូវបានបិទសំឡេង។"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"រុករកដោយប្រើក្ដារចុច និងផ្ទាំងប៉ះរបស់អ្នក"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ស្វែងយល់អំពីចលនាផ្ទាំងប៉ះ ផ្លូវកាត់ក្ដារចុច និងអ្វីៗជាច្រើនទៀត"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ថយក្រោយ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ទៅទំព័រដើម"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"មើលកម្មវិធីថ្មីៗ"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"រួចរាល់"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ថយក្រោយ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"អូសទៅឆ្វេង ឬស្ដាំដោយប្រើម្រាមដៃបីនៅលើផ្ទាំងប៉ះរបស់អ្នក"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ល្អ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"អ្នកបានបញ្ចប់ចលនាថយក្រោយហើយ។"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ទៅទំព័រដើម"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"អូសឡើងលើដោយប្រើម្រាមដៃបីនៅលើផ្ទាំងប៉ះរបស់អ្នក"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ធ្វើបានល្អ!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"អ្នកបានបញ្ចប់ចលនាចូលទៅកាន់ទំព័រដើមហើយ"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"មើលកម្មវិធីថ្មីៗ"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"អូសឡើងលើ ហើយសង្កត់ឱ្យជាប់ដោយប្រើម្រាមដៃបីលើផ្ទាំងប៉ះរបស់អ្នក"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ធ្វើបានល្អ!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"អ្នកបានបញ្ចប់ការមើលចលនាកម្មវិធីថ្មីៗ។"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"មើលកម្មវិធីទាំងអស់"</string> + <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="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 5ac910256f04..57a8cdf2fa66 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"ಸೆಟ್ ಮಾಡಿಲ್ಲ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ನಿರ್ವಹಿಸಿ"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ಯಾವುದೇ ಸಕ್ರಿಯ ಮೋಡ್ಗಳಿಲ್ಲ}=1{{mode} ಸಕ್ರಿಯವಾಗಿದೆ}one{# ಮೋಡ್ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ}other{# ಮೋಡ್ಗಳು ಸಕ್ರಿಯವಾಗಿವೆ}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"ಅಲಾರಾಂಗಳು, ಜ್ಞಾಪನೆಗಳು, ಈವೆಂಟ್ಗಳು ಹಾಗೂ ನೀವು ಸೂಚಿಸಿರುವ ಕರೆದಾರರನ್ನು ಹೊರತುಪಡಿಸಿ ಬೇರಾವುದೇ ಸದ್ದುಗಳು ಅಥವಾ ವೈಬ್ರೇಶನ್ಗಳು ನಿಮಗೆ ತೊಂದರೆ ನೀಡುವುದಿಲ್ಲ. ಹಾಗಿದ್ದರೂ, ನೀವು ಪ್ಲೇ ಮಾಡುವ ಸಂಗೀತ, ವೀಡಿಯೊಗಳು ಮತ್ತು ಆಟಗಳ ಆಡಿಯೊವನ್ನು ನೀವು ಕೇಳಿಸಿಕೊಳ್ಳುತ್ತೀರಿ."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ರಿಂಗ್ ಮ್ಯೂಟ್ ಆಗಿರುವ ಕಾರಣ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಫೀಚರ್ ಆನ್ ಆಗಿರುವುದರಿಂದ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಫೀಚರ್ ಆನ್ ಆಗಿರುವುದರಿಂದ ಲಭ್ಯವಿಲ್ಲ"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ಅನ್ಮ್ಯೂಟ್ ಮಾಡುವುದಕ್ಕಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ಕಂಪನಕ್ಕೆ ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ಮ್ಯೂಟ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೇವೆಗಳನ್ನು ಮ್ಯೂಟ್ ಮಾಡಬಹುದು."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ಟಚ್ಪ್ಯಾಡ್ ಗೆಸ್ಚರ್ಗಳನ್ನು ಕಲಿಯಿರಿ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಮತ್ತು ಟಚ್ಪ್ಯಾಡ್ ಬಳಸಿ ನ್ಯಾವಿಗೇಟ್ ಮಾಡಿ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ಟಚ್ಪ್ಯಾಡ್ ಗೆಸ್ಚರ್ಗಳು, ಕೀಬೋರ್ಡ್ಗಳ ಶಾರ್ಟ್ಕಟ್ಗಳು ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ತಿಳಿಯಿರಿ"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ಹಿಂದಿರುಗಿ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ಮುಗಿದಿದೆ"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ಹಿಂತಿರುಗಿ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ನಿಮ್ಮ ಟಚ್ಪ್ಯಾಡ್ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಎಡ ಅಥವಾ ಬಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ಚೆನ್ನಾಗಿದೆ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ನೀವು ಗೋ ಬ್ಯಾಕ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ಟಚ್ಪ್ಯಾಡ್ನಲ್ಲಿ ಮೂರು ಬೆರಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ಭೇಷ್!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ನಿಮ್ಮ ಟಚ್ಪ್ಯಾಡ್ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ಭೇಷ್!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ನೀವು ಇತ್ತೀಚಿನ ಆ್ಯಪ್ಗಳ ಗೆಸ್ಚರ್ ವೀಕ್ಷಣೆಯನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string> + <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="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 b7d81b41ac87..9775ad5d9bf1 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"설정되지 않음"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"설정에서 관리"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{활성화된 모드 없음}=1{{mode} 모드가 활성화됨}other{모드 #개가 활성화됨}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"알람, 알림, 일정 및 지정한 발신자로부터 받은 전화를 제외한 소리와 진동을 끕니다. 음악, 동영상, 게임 등 재생하도록 선택한 소리는 정상적으로 들립니다."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"벨소리가 음소거되어 있으므로 사용할 수 없음"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"방해 금지 모드로 설정되어 있어 사용할 수 없음"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"방해 금지 모드로 설정되어 있어 사용할 수 없음"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. 탭하여 음소거를 해제하세요."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. 탭하여 진동으로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. 탭하여 음소거로 설정하세요. 접근성 서비스가 음소거될 수 있습니다."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"터치패드 동작 알아보기"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"키보드와 터치패드를 사용하여 이동"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"터치패드 동작, 단축키 등 알아보기"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"뒤로 이동"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"홈으로 이동"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"최근 앱 보기"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"완료"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"뒤로"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"세 손가락을 사용해 터치패드에서 왼쪽 또는 오른쪽으로 스와이프하세요."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"훌륭합니다"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"돌아가기 동작을 완료했습니다."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"홈으로 이동"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"세 손가락을 사용해 터치패드에서 위로 스와이프하세요."</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"아주 좋습니다"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"홈으로 이동 동작을 완료했습니다."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"최근 앱 보기"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"세 손가락을 사용해 터치패드에서 위로 스와이프한 후 잠시 기다리세요."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"아주 좋습니다"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"최근 앱 보기 동작을 완료했습니다."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"모든 앱 보기"</string> + <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="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 da4d4c38a57a..80b75b933cca 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Туураланган эмес"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Параметрлерден тескөө"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Жигердүү режимдер жок}=1{{mode} иштеп жатат}other{# режим иштеп жатат}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Ойготкучтардан, эскертүүлөрдөн, жылнаамадагы иш-чараларды эстеткичтерден жана белгиленген байланыштардын чалууларынан тышкары башка үндөр жана дирилдөөлөр тынчыңызды албайт. Бирок ойнотулуп жаткан музыканы, видеолорду жана оюндарды мурдагыдай эле уга бересиз."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Үнсүз режимде жеткиликсиз"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\"Тынчымды алба\" режими күйүк болгондуктан, жеткиликсиз"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\"Тынчымды алба\" режими күйүк болгондуктан, жеткиликсиз"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Үнүн чыгаруу үчүн таптап коюңуз."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Дирилдөөгө коюу үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Үнүн өчүрүү үчүн таптап коюңуз. Атайын мүмкүнчүлүктөр кызматынын үнүн өчүрүп койсо болот."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Сенсордук тактадагы жаңсоолорду үйрөнүп алыңыз"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Нерселерге баскычтоп жана сенсордук такта аркылуу өтүңүз"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Сенсордук тактадагы жаңсоолор, ыкчам баскычтар жана башкалар жөнүндө билип алыңыз"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Артка кайтуу"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Башкы бетке өтүү"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Акыркы колдонмолорду көрүү"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Бүттү"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Артка кайтуу"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Сенсордук тактаны үч манжаңыз менен солго же оңго сүрүңүз"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Сонун!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"\"Артка\" жаңсоосу боюнча үйрөткүчтү бүтүрдүңүз."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Башкы бетке өтүү"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Сенсордук тактаны үч манжаңыз менен жогору сүрүңүз"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Азаматсыз!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"\"Башкы бетке өтүү\" жаңсоосун үйрөндүңүз"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Акыркы колдонмолорду көрүү"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Сенсордук тактаны үч манжаңыз менен өйдө сүрүп, кармап туруңуз"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Азаматсыз!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Акыркы колдонмолорду көрүү жаңсоосун аткардыңыз."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бардык колдонмолорду көрүү"</string> + <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="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-land/config.xml b/packages/SystemUI/res/values-land/config.xml index b5efeb5f6b3b..5d5b95546d0d 100644 --- a/packages/SystemUI/res/values-land/config.xml +++ b/packages/SystemUI/res/values-land/config.xml @@ -25,6 +25,12 @@ <integer name="quick_settings_num_columns">4</integer> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">2</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">1</integer> + <!-- The number of columns in the infinite grid QuickSettings --> <integer name="quick_settings_infinite_grid_num_columns">8</integer> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 92b39745ab73..3a09b2a25995 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ບໍ່ມີໃຫ້ໃຊ້ເນື່ອງຈາກມີການປິດສຽງໂທເຂົ້າ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ບໍ່ມີໃຫ້ຍ້ອນວ່າຫ້າມລົບກວນເປີດຢູ່"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ບໍ່ມີໃຫ້ຍ້ອນວ່າຫ້າມລົບກວນເປີດຢູ່"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ແຕະເພື່ອເຊົາປິດສຽງ."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. ແຕະເພື່ອຕັ້ງເປັນສັ່ນ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ແຕະເພື່ອປິດສຽງ. ບໍລິການຊ່ວຍເຂົ້າເຖິງອາດຖືກປິດສຽງໄວ້."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index a5e325e28fad..f3f383e4d418 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Įjungta"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Įjungta • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Išjungta"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nenustatyta"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Tvarkyti skiltyje „Nustatymai“"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nėra aktyvių režimų}=1{Aktyvus režimas „{mode}“}one{# aktyvus režimas}few{# aktyvūs režimai}many{# aktyvaus režimo}other{# aktyvių režimų}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Jūsų netrikdys garsai ir vibravimas, išskyrus nurodytų signalų, priminimų, įvykių ir skambintojų garsus. Vis tiek girdėsite viską, ką pasirinksite leisti, įskaitant muziką, vaizdo įrašus ir žaidimus."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nepasiekiama, nes skambutis nutildytas"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nepasiekiama, nes įjungtas netrukdymo režimas"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nepasiekiama, nes įjungtas netrukdymo režimas"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Palieskite, kad įjungtumėte garsą."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Palieskite, kad nustatytumėte vibravimą. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Palieskite, kad nutildytumėte. Gali būti nutildytos pritaikymo neįgaliesiems paslaugos."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Sužinokite jutiklinės dalies gestus"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naršykite naudodamiesi klaviatūra ir jutikline dalimi"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sužinokite jutiklinės dalies gestus, sparčiuosius klavišus ir kt."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Grįžti"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Eikite į pagrindinį puslapį"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Peržiūrėti naujausias programas"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Atlikta"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Grįžti"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Braukite kairėn arba dešinėn trimis pirštais bet kur jutiklinėje dalyje"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Šaunu!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Atlikote grįžimo atgal gestą."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Eikite į pagrindinį ekraną"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Braukite viršun trimis pirštais bet kur jutiklinėje dalyje"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Puiku!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Atlikote perėjimo į pagrindinį ekraną gestą"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Peržiūrėti naujausias programas"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Braukite viršun trimis pirštais bet kur jutiklinėje dalyje ir palaikykite"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Puiku!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Atlikote naujausių programų peržiūros gestą."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Žr. visas programas"</string> + <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="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 b570ad6f700f..9c22eed6bbd9 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Ieslēgts"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ieslēgts • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Izslēgts"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nav iestatīts"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Pārvaldīt iestatījumos"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nav aktīvu režīmu}=1{Režīms “{mode}” ir aktīvs}zero{# režīmi ir aktīvi}one{# režīms ir aktīvs}other{# režīmi ir aktīvi}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Jūs netraucēs skaņas un vibrācija, izņemot signālus, atgādinājumus, pasākumus un zvanītājus, ko būsiet norādījis. Jūs joprojām dzirdēsiet atskaņošanai izvēlētos vienumus, tostarp mūziku, videoklipus un spēles."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nevar mainīt, jo izslēgts skaņas signāls"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nav pieejams, jo ir ieslēgts režīms Netraucēt"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nav pieejams, jo ir ieslēgts režīms Netraucēt"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Pieskarieties, lai ieslēgtu skaņu."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Pieskarieties, lai iestatītu uz vibrozvanu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Pieskarieties, lai izslēgtu skaņu. Var tikt izslēgti pieejamības pakalpojumu signāli."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Rādīt demonstrācijas režīmu"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Tīkls Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Signāls"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"Režīms “<xliff:g id="MODENAME">%1$s</xliff:g>” ir ieslēgts"</string> <string name="wallet_title" msgid="5369767670735827105">"Maks"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Iestatiet, lai ātrāk un drošāk veiktu pirkumus, izmantojot tālruni"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Rādīt visu"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Apgūstiet skārienpaliktņa žestus."</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Pārvietošanās, izmantojot tastatūru un skārienpaliktni"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Uzziniet par skārienpaliktņa žestiem, īsinājumtaustiņiem un citām iespējām."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atpakaļ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pāriet uz sākuma ekrānu"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Skatīt nesen izmantotās lietotnes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gatavs"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atpakaļ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Skārienpaliktnī ar trīs pirkstiem velciet pa kreisi vai pa labi."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Lieliski!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Jūs sekmīgi veicāt atgriešanās žestu."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Pāreja uz sākuma ekrānu"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Skārienpaliktnī ar trīs pirkstiem velciet augšup."</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Lieliski!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Jūs sekmīgi veicāt sākuma ekrāna atvēršanas žestu."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Nesen izmantoto lietotņu skatīšana"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Skārienpaliktnī ar trīs pirkstiem velciet augšup un turiet."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Lieliski!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Jūs sekmīgi veicāt nesen izmantoto lietotņu skatīšanas žestu."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Skatīt visas lietotnes"</string> + <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> <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 708135dbc006..6e37907b6ffb 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недостапно бидејќи звукот е исклучен"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недостапно бидејќи е вклучено „Не вознемирувај“"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Допрете за да вклучите звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Допрете за да поставите на вибрации. Можеби ќе се исклучи звукот на услугите за достапност."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Допрете за да исклучите звук. Можеби ќе се исклучи звукот на услугите за достапност."</string> @@ -706,8 +710,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Прикажи демо-режим"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Етернет"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Аларм"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"Режимот „<xliff:g id="MODENAME">%1$s</xliff:g>“ е вклучен"</string> <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Поставете за да купувате побрзо и побезбедно преку вашиот телефон"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Прикажи ги сите"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 28fd77d5ac0b..bf446d988eb7 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"സജ്ജീകരിച്ചിട്ടില്ല"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ക്രമീകരണത്തിൽ മാനേജ് ചെയ്യുക"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{സജീവ മോഡുകൾ ഒന്നുമില്ല}=1{{mode} സജീവമാണ്}other{# മോഡുകൾ സജീവമാണ്}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"നിങ്ങൾ സജ്ജീകരിച്ച അലാറങ്ങൾ, റിമൈൻഡറുകൾ, ഇവന്റുകൾ, കോളർമാർ എന്നിവയിൽ നിന്നുള്ള ശബ്ദങ്ങളും വൈബ്രേഷനുകളുമൊഴികെ മറ്റൊന്നും നിങ്ങളെ ശല്യപ്പെടുത്തുകയില്ല. സംഗീതം, വീഡിയോകൾ, ഗെയിമുകൾ എന്നിവയുൾപ്പെടെ പ്ലേ ചെയ്യുന്നതെന്തും നിങ്ങൾക്ക് തുടർന്നും കേൾക്കാൻ കഴിയും."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"റിംഗ് മ്യൂട്ട് ചെയ്തതിനാൽ ലഭ്യമല്ല"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ശല്യപ്പെടുത്തരുത് ഓണായതിനാൽ ലഭ്യമല്ല"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ശല്യപ്പെടുത്തരുത് ഓണായതിനാൽ ലഭ്യമല്ല"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. അൺമ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. വൈബ്രേറ്റിലേക്ക് സജ്ജമാക്കുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. മ്യൂട്ടുചെയ്യുന്നതിന് ടാപ്പുചെയ്യുക. ഉപയോഗസഹായി സേവനങ്ങൾ മ്യൂട്ടുചെയ്യപ്പെട്ടേക്കാം."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ടച്ച്പാഡ് ജെസ്ച്ചറുകൾ മനസ്സിലാക്കുക"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"നിങ്ങളുടെ കീപാഡ്, ടച്ച്പാഡ് എന്നിവ ഉപയോഗിച്ച് നാവിഗേറ്റ് ചെയ്യുക"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ടച്ച്പാഡ് ജെസ്ച്ചറുകൾ, കീബോർഡ് കുറുക്കുവഴികൾ എന്നിവയും മറ്റും മനസ്സിലാക്കുക"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"മടങ്ങുക"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ഹോമിലേക്ക് പോകുക"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"പൂർത്തിയായി"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"മടങ്ങുക"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ കൊണ്ട് ഇടത്തേക്കോ വലത്തേക്കോ സ്വൈപ്പ് ചെയ്യുക"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"കൊള്ളാം!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"മടങ്ങുക ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ഹോമിലേക്ക് പോകൂ"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ ഉപയോഗിച്ച് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"കൊള്ളാം!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ഹോം ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"നിങ്ങളുടെ ടച്ച്പാഡിൽ മൂന്ന് വിരലുകൾ കൊണ്ട് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്ത് പിടിക്കുക"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"കൊള്ളാം!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"അടുത്തിടെയുള്ള ആപ്പുകൾ കാണുക എന്ന ജെസ്ച്ചർ നിങ്ങൾ പൂർത്തിയാക്കി."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"എല്ലാ ആപ്പുകളും കാണുക"</string> + <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="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 dfd239838f54..1bd6768e93de 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Хонхны дууг хаасан тул боломжгүй"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Бүү саад бол асаалттай тул боломжгүй"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Бүү саад бол асаалттай тул боломжгүй"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Дууг нь нээхийн тулд товшино уу."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Чичиргээнд тохируулахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Дууг нь хаахын тулд товшино уу. Хүртээмжийн үйлчилгээний дууг хаасан."</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index db2ed93da0a9..60718b9e4172 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट केलेले नाही"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिंग्जमध्ये व्यवस्थापित करा"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कोणतेही अॅक्टिव्ह मोड नाहीत}=1{{mode} अॅक्टिव्ह आहे}other{# मोड अॅक्टिव्ह आहेत}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"अलार्म, रिमाइंडर, इव्हेंट आणि तुम्ही निश्चित केलेल्या कॉलर व्यतिरिक्त तुम्हाला कोणत्याही आवाज आणि कंपनांचा व्यत्त्यय आणला जाणार नाही. तरीही तुम्ही प्ले करायचे ठरवलेले कोणतेही संगीत, व्हिडिओ आणि गेमचे आवाज ऐकू शकतात."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"रिंग म्यूट केल्यामुळे उपलब्ध नाही"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"व्यत्यय आणू नका हे सुरू असल्यामुळे उपलब्ध नाही"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"व्यत्यय आणू नका हे सुरू असल्यामुळे उपलब्ध नाही"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. अनम्यूट करण्यासाठी टॅप करा."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. व्हायब्रेट सेट करण्यासाठी टॅप करा. प्रवेशयोग्यता सेवा म्यूट केल्या जाऊ शकतात."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. म्यूट करण्यासाठी टॅप करा. प्रवेशक्षमता सेवा म्यूट केल्या जाऊ शकतात."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचपॅड जेश्चर जाणून घ्या"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"तुमचा कीबोर्ड आणि टचपॅड वापरून नेव्हिगेट करा"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचपॅड जेश्चर, कीबोर्ड शॉर्टकट आणि आणखी बरेच काही जाणून घ्या"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"मागे जा"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होमवर जा"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"अलीकडील अॅप्स पहा"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"पूर्ण झाले"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"मागे जा"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"तुमच्या टचपॅडवर तीन बोटांनी डावीकडे किंवा उजवीकडे स्वाइप करा"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"छान!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तुम्ही गो बॅक जेश्चर पूर्ण केले."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमवर जा"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"तुमच्या टचपॅडवर तीन बोटांनी वर स्वाइप करा"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"उत्तम कामगिरी!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"तुम्ही गो होम जेश्चर पूर्ण केले आहे"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"अलीकडील अॅप्स पहा"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"तुमच्या टचपॅडवर तीन बोटांनी वर स्वाइप करून धरून ठेवा"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"उत्तम कामगिरी!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तुम्ही अलीकडील ॲप्स पाहण्याचे जेश्चर पूर्ण केले आहे."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"सर्व अॅप्स पहा"</string> + <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="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 abdb32fceebc..1160a1af486e 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Tidak tersedia kerana deringan diredam"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Tidak tersedia kerana Jangan Ganggu dihidupkan"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Tidak tersedia kerana Jangan Ganggu dihidupkan"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ketik untuk menyahredam."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Ketik untuk menetapkan pada getar. Perkhidmatan kebolehaksesan mungkin diredamkan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ketik untuk meredam. Perkhidmatan kebolehaksesan mungkin diredamkan."</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index f4f337b05dee..224247eab689 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"သတ်မှတ်မထားပါ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ဆက်တင်များတွင် စီမံရန်"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{သုံးနေသော မုဒ်မရှိပါ}=1{{mode} ကို သုံးနေသည်}other{မုဒ် # ခု သုံးနေသည်}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"နှိုးစက်သံ၊ သတိပေးချက်အသံများ၊ ပွဲစဉ်သတိပေးသံများနှင့် သင်ခွင့်ပြုထားသူများထံမှ ဖုန်းခေါ်မှုများမှလွဲ၍ အခြားအသံများနှင့် တုန်ခါမှုများက သင့်ကို အနှောင့်အယှက်ပြုမည် မဟုတ်ပါ။ သို့သော်လည်း သီချင်း၊ ဗီဒီယိုနှင့် ဂိမ်းများအပါအဝင် သင်ကရွေးချယ်ဖွင့်ထားသည့် အရာတိုင်း၏ အသံကိုမူ ကြားနေရဆဲဖြစ်ပါလိမ့်မည်။"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ဖုန်းမြည်သံပိတ်ထားသဖြင့် မရနိုင်ပါ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"‘မနှောင့်ယှက်ရ’ ဖွင့်ထားသောကြောင့် မရနိုင်ပါ"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s။ အသံပြန်ဖွင့်ရန် တို့ပါ။"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s။ တုန်ခါမှုကို သတ်မှတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s။ အသံပိတ်ရန် တို့ပါ။ အများသုံးနိုင်မှု ဝန်ဆောင်မှုများကို အသံပိတ်ထားနိုင်ပါသည်။"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"တာ့ချ်ပက်လက်ဟန်များကို လေ့လာပါ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"သင်၏ ကီးဘုတ်နှင့် တာ့ချ်ပက်တို့ကိုသုံး၍ လမ်းညွှန်ခြင်း"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"တာ့ချ်ပက်လက်ဟန်များ၊ လက်ကွက်ဖြတ်လမ်းများ စသည်တို့ကို လေ့လာပါ"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"နောက်သို့"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ပင်မစာမျက်နှာသို့ သွားရန်"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"မကြာသေးမီကအက်ပ်များကို ကြည့်ရန်"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ပြီးပြီ"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ပြန်သွားရန်"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"သင့်တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် ဘယ် (သို့) ညာသို့ ပွတ်ဆွဲပါ"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ကောင်းပါသည်။"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"နောက်သို့လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ပင်မစာမျက်နှာသို့ သွားရန်"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"တာ့ချ်ပက်ပေါ်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ ပွတ်ဆွဲပါ"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"တော်ပါပေသည်။"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ပင်မစာမျက်နှာသို့သွားသည့် လက်ဟန် အပြီးသတ်လိုက်ပါပြီ"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်း"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"သင့်တာ့ချ်ပက်တွင် လက်သုံးချောင်းဖြင့် အပေါ်သို့ပွတ်ဆွဲပြီး ဖိထားပါ"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"တော်ပါပေသည်။"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"မကြာသေးမီကအက်ပ်များကို ကြည့်ခြင်းလက်ဟန် သင်ခန်းစာပြီးပါပြီ။"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"အက်ပ်အားလုံးကို ကြည့်ရန်"</string> + <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="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 f98799aade14..6d65c6c1640d 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"På"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"På • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Av"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Ikke angitt"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Administrer i innstillingene"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Ingen aktive moduser}=1{{mode} er aktiv}other{# moduser er aktive}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Du blir ikke forstyrret av lyder og vibrasjoner, med unntak av alarmer, påminnelser, aktiviteter og oppringere du angir. Du kan fremdeles høre alt du velger å spille av, for eksempel musikk, videoer og spill."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Utilgjengelig fordi ringelyden er kuttet"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Utilgjengelig fordi «Ikke forstyrr» er på"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Utilgjengelig fordi «Ikke forstyrr» er på"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trykk for å slå på lyden."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trykk for å angi vibrasjon. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trykk for å slå av lyden. Lyden kan bli slått av for tilgjengelighetstjenestene."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Vis demo-modus"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> er på"</string> <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Legg til en betalingsmåte for å gjennomføre kjøp raskere og sikrere med telefonen"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Vis alle"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Lær deg styreflatebevegelser"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Naviger med tastaturet og styreflaten"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Lær deg styreflatebevegelser, hurtigtaster med mer"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Gå tilbake"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Gå til startsiden"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Se nylige apper"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Ferdig"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbake"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Sveip til venstre eller høyre med tre fingre på styreflaten"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bra!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har fullført bevegelsen for å gå tilbake."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gå til startsiden"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Sveip opp med tre fingre på styreflaten"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Bra jobbet!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Du har fullført bevegelsen for å gå til startskjermen"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Se nylige apper"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Sveip opp og hold med tre fingre på styreflaten"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Bra jobbet!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Du har fullført bevegelsen for å se nylige apper."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Se alle apper"</string> + <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> <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> @@ -1465,7 +1455,7 @@ <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Snarveiene for ekstra dimming er fjernet"</string> <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Tilkobling"</string> <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Tilgjengelighet"</string> - <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Systemverktøy"</string> + <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Verktøy"</string> <string name="qs_edit_mode_category_privacy" msgid="6577774443194551775">"Personvern"</string> <string name="qs_edit_mode_category_providedByApps" msgid="8346112074897919019">"Levert av apper"</string> <string name="qs_edit_mode_category_display" msgid="4749511439121053942">"Skjerm"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 2c8778f8cec6..a55c53a74042 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"सेट गरिएको छैन"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"सेटिङमा गई व्यवस्थापन गर्नुहोस्"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{कुनै पनि सक्रिय छैन}=1{{mode} सक्रिय छ}other{# मोड सक्रिय छन्}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"तपाईंलाई अलार्म, रिमाइन्डर, कार्यक्रम र तपाईंले निर्दिष्ट गर्नुभएका कलरहरू बाहेकका ध्वनि र कम्पनहरूले बाधा पुऱ्याउने छैनन्। तपाईंले अझै सङ्गीत, भिडियो र खेलहरू लगायत आफूले प्ले गर्न छनौट गरेका जुनसुकै कुरा सुन्न सक्नुहुनेछ।"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"डिभाइस म्युट गरिएकाले यो सुविधा उपलब्ध छैन"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Do Not Disturb अन भएकाले भोल्युम बढाउन मिल्दैन"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Do Not Disturb अन भएकाले भोल्युम बढाउन मिल्दैन"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। अनम्यूट गर्नाका लागि ट्याप गर्नुहोस्।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। कम्पनमा सेट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। म्यूट गर्नाका लागि ट्याप गर्नुहोस्। पहुँच सम्बन्धी सेवाहरू म्यूट हुन सक्छन्।"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"टचप्याड जेस्चर प्रयोग गर्न सिक्नुहोस्"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"किबोर्ड र टचप्याड प्रयोग गरी नेभिगेट गर्नुहोस्"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"टचप्याड जेस्चर, किबोर्डका सर्टकट र अन्य कुरा प्रयोग गर्न सिक्नुहोस्"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"पछाडि जानुहोस्"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"होम स्क्रिनमा जानुहोस्"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"सम्पन्न भयो"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"तीन वटा औँला प्रयोग गरी टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"राम्रो!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमपेजमा जानुहोस्"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"टचप्याडमा तीन वटा औँलाले माथितिर स्वाइप गर्नुहोस्"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"अद्भुत!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"तपाईंले \"होम स्क्रिनमा जानुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"तीन वटा औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"अद्भुत!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले हालसालै चलाइएका एपहरू हेर्ने जेस्चर पूरा गर्नुभएको छ।"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"सबै एपहरू हेर्नुहोस्"</string> + <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="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 cd20a29329d7..e5bc5be40235 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Aan"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Aan • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Uit"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Niet ingesteld"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Beheren via instellingen"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Geen actieve modi}=1{{mode} is actief}other{# modi zijn actief}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Je wordt niet gestoord door geluiden en trillingen, behalve bij wekkers, herinneringen, afspraken en specifieke bellers die je selecteert. Je kunt nog steeds alles horen wat je wilt afspelen, waaronder muziek, video\'s en games."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niet beschikbaar, belgeluid staat uit"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niet beschikbaar omdat Niet storen aanstaat"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Niet beschikbaar omdat Niet storen aanstaat"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tik om dempen op te heffen."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tik om in te stellen op trillen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tik om te dempen. Het geluid van toegankelijkheidsservices kan hierdoor uitgaan."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Leer touchpadgebaren die je kunt gebruiken"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigeren met je toetsenbord en touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Leer meer over onder andere touchpadgebaren en sneltoetsen"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Terug"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Naar startscherm"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Recente apps bekijken"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Klaar"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Terug"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swipe met 3 vingers naar links of rechts op de touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Goed zo!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Je weet nu hoe je het gebaar voor terug maakt."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Naar startscherm"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe met 3 vingers omhoog op de touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Goed werk!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Je weet nu hoe je het gebaar Naar startscherm maakt"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Recente apps bekijken"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe met 3 vingers omhoog en houd vast op de touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed werk!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle apps bekijken"</string> + <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> <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 59e64621cbae..cf7a58d763c9 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"ସେଟ କରାଯାଇନାହିଁ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ସେଟିଂସରେ ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{କୌଣସି ସକ୍ରିୟ ମୋଡ ନାହିଁ}=1{{mode} ସକ୍ରିୟ ଅଛି}other{# ମୋଡ ସକ୍ରିୟ ଅଛି}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍, ଭିଡିଓ ଏବଂ ଗେମ୍ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ରିଂକୁ ମ୍ୟୁଟ କରାଯାଇଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\' ଚାଲୁ ଥିବା ଯୋଗୁଁ ଉପଲବ୍ଧ ନାହିଁ"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ଅନମ୍ୟୁଟ୍ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ଭାଇବ୍ରେଟ୍ ସେଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ମ୍ୟୁଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍ ମ୍ୟୁଟ୍ କରାଯାଇପାରେ।"</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"ଡେମୋ ମୋଡ୍ ଦେଖାନ୍ତୁ"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"ଇଥରନେଟ୍"</string> <string name="status_bar_alarm" msgid="87160847643623352">"ଆଲାରାମ"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> ଚାଲୁ ଅଛି"</string> <string name="wallet_title" msgid="5369767670735827105">"ୱାଲେଟ୍"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"ଆପଣଙ୍କ ଫୋନ୍ ମାଧ୍ୟମରେ ଆହୁରି ଶୀଘ୍ର, ଅଧିକ ସୁରକ୍ଷିତ କ୍ରୟ କରିବା ପାଇଁ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"ସବୁ ଦେଖାନ୍ତୁ"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ଟଚପେଡର ଜେଶ୍ଚରଗୁଡ଼ିକ ବିଷୟରେ ଜାଣନ୍ତୁ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ଆପଣଙ୍କ କୀବୋର୍ଡ ଏବଂ ଟଚପେଡ ବ୍ୟବହାର କରି ନାଭିଗେଟ କରନ୍ତୁ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ଟଚପେଡ ଜେଶ୍ଚର, କୀବୋର୍ଡ ସର୍ଟକଟ ଏବଂ ଆହୁରି ଅନେକ କିଛି ବିଷୟରେ ଜାଣନ୍ତୁ"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ପଛକୁ ଫେରନ୍ତୁ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ହୋମକୁ ଯାଆନ୍ତୁ"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ହୋଇଗଲା"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ପଛକୁ ଫେରନ୍ତୁ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠି ବ୍ୟବହାର କରି ବାମ କିମ୍ବା ଡାହାଣକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ବଢ଼ିଆ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ଆପଣ \'ପଛକୁ ଫେରନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ହୋମକୁ ଯାଆନ୍ତୁ"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିରେ ଉପରକୁ ସ୍ୱାଇପ କରନ୍ତୁ"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ବଢ଼ିଆ କାମ!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ଆପଣ \'ହୋମକୁ ଯାଆନ୍ତୁ\' ଜେଶ୍ଚର ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ବର୍ତ୍ତମାନର ଆପ୍ସ ଭ୍ୟୁ କରନ୍ତୁ"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ଆପଣଙ୍କ ଟଚପେଡରେ ତିନୋଟି ଆଙ୍ଗୁଠିକୁ ବ୍ୟବହାର କରି ଉପରକୁ ସ୍ୱାଇପ କରି ଧରି ରଖନ୍ତୁ"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ବଢ଼ିଆ କାମ!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ଆପଣ ବର୍ତ୍ତମାନର ଆପ୍ସ ଜେଶ୍ଚରକୁ ଭ୍ୟୁ କରିବା ସମ୍ପୂର୍ଣ୍ଣ କରିଛନ୍ତି।"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"ସମସ୍ତ ଆପ ଭ୍ୟୁ କରନ୍ତୁ"</string> + <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="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 4c6697d39f81..43691a4bef1b 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"ਸੈੱਟ ਨਹੀਂ ਹੈ"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ਕੋਈ ਕਿਰਿਆਸ਼ੀਲ ਮੋਡ ਨਹੀਂ ਹੈ}=1{{mode} ਕਿਰਿਆਸ਼ੀਲ ਹੈ}other{# ਮੋਡ ਕਿਰਿਆਸ਼ੀਲ ਹਨ}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"ਧੁਨੀਆਂ ਅਤੇ ਥਰਥਰਾਹਟਾਂ ਤੁਹਾਨੂੰ ਪਰੇਸ਼ਾਨ ਨਹੀਂ ਕਰਨਗੀਆਂ, ਸਿਵਾਏ ਅਲਾਰਮਾਂ, ਯਾਦ-ਦਹਾਨੀਆਂ, ਵਰਤਾਰਿਆਂ, ਅਤੇ ਤੁਹਾਡੇ ਵੱਲੋਂ ਨਿਰਧਾਰਤ ਕੀਤੇ ਕਾਲਰਾਂ ਦੀ ਸੂਰਤ ਵਿੱਚ। ਤੁਸੀਂ ਅਜੇ ਵੀ ਸੰਗੀਤ, ਵੀਡੀਓ ਅਤੇ ਗੇਮਾਂ ਸਮੇਤ ਆਪਣੀ ਚੋਣ ਅਨੁਸਾਰ ਕੁਝ ਵੀ ਸੁਣ ਸਕਦੇ ਹੋ।"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਘੰਟੀ ਮਿਊਟ ਕੀਤੀ ਹੋਈ ਹੈ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਵਿਸ਼ੇਸ਼ਤਾ ਚਾਲੂ ਹੈ"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ ਕਿਉਂਕਿ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਵਿਸ਼ੇਸ਼ਤਾ ਚਾਲੂ ਹੈ"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s। ਅਣਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s। ਥਰਥਰਾਹਟ ਸੈੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s। ਮਿਊਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ। ਪਹੁੰਚਯੋਗਤਾ ਸੇਵਾਵਾਂ ਮਿਊਟ ਹੋ ਸਕਦੀਆਂ ਹਨ।"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ਟੱਚਪੈਡ ਇਸ਼ਾਰਿਆਂ ਬਾਰੇ ਜਾਣੋ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ਆਪਣੇ ਕੀ-ਬੋਰਡ ਅਤੇ ਟੱਚਪੈਡ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਨੈਵੀਗੇਟ ਕਰੋ"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ਟੱਚਪੈਡ ਇਸ਼ਾਰੇ, ਕੀ-ਬੋਰਡ ਸ਼ਾਰਟਕੱਟ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਬਾਰੇ ਜਾਣੋ"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ਵਾਪਸ ਜਾਓ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ਹੋਮ \'ਤੇ ਜਾਓ"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ਹੋ ਗਿਆ"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ਵਾਪਸ ਜਾਓ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਖੱਬੇ ਜਾਂ ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ਵਧੀਆ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ਤੁਸੀਂ \'ਵਾਪਸ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ਹੋਮ \'ਤੇ ਜਾਓ"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਨਾਲ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ਬਹੁਤ ਵਧੀਆ!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ਤੁਸੀਂ \'ਹੋਮ \'ਤੇ ਜਾਓ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ਆਪਣੇ ਟੱਚਪੈਡ \'ਤੇ ਤਿੰਨ ਉਂਗਲਾਂ ਦੀ ਵਰਤੋਂ ਕਰ ਕੇ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ਬਹੁਤ ਵਧੀਆ!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ਤੁਸੀਂ \'ਹਾਲੀਆ ਐਪਾਂ ਦੇਖੋ\' ਦਾ ਇਸ਼ਾਰਾ ਪੂਰਾ ਕੀਤਾ ਹੈ।"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"ਸਾਰੀਆਂ ਐਪਾਂ ਦੇਖੋ"</string> + <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="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 f4efe0b5a985..3e1e2d13f9b7 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Wł."</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Włączone • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Wył."</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nie ustawiono"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Zarządzaj w ustawieniach"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Brak aktywnych trybów}=1{Tryb {mode} jest aktywny}few{# tryby są aktywne}many{# trybów jest aktywnych}other{# trybu jest aktywne}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Nie będą Cię niepokoić żadne dźwięki ani wibracje z wyjątkiem alarmów, przypomnień, wydarzeń i połączeń od wybranych osób. Będziesz słyszeć wszystkie odtwarzane treści, takie jak muzyka, filmy czy gry."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Niedostępne, bo dzwonek jest wyciszony"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Niedostępne, włączone jest „Nie przeszkadzać”"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Niedostępne, włączone jest „Nie przeszkadzać”"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Kliknij, by wyłączyć wyciszenie."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Kliknij, by włączyć wibracje. Ułatwienia dostępu mogą być wyciszone."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Kliknij, by wyciszyć. Ułatwienia dostępu mogą być wyciszone."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Poznaj gesty na touchpada"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Nawiguj za pomocą klawiatury i touchpada"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Poznaj gesty na touchpada, skróty klawiszowe i inne funkcje"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Wróć"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Otwórz stronę główną"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Wyświetlanie ostatnich aplikacji"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gotowe"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Wróć"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Przesuń 3 palcami w prawo lub w lewo na touchpadzie"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Super!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Gest przejścia wstecz został opanowany."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Otwórz stronę główną"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Przesuń 3 palcami w górę na touchpadzie"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Dobra robota!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Gest przechodzenia na ekran główny został opanowany"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Wyświetlanie ostatnich aplikacji"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Przesuń w górę za pomocą 3 palców na touchpadzie i przytrzymaj"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Brawo!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Znasz już gest wyświetlania ostatnio używanych aplikacji."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Wyświetl wszystkie aplikacje"</string> + <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> <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 b27242248da2..c34f2f72a53d 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Não definido"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerenciar nas configurações"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nenhum modo ativo}=1{{mode} está ativo}one{# modo está ativo}many{# de modos estão ativos}other{# modos estão ativos}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível com o Não perturbe ativado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com o Não perturbe ativado"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demonstração"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"O modo <xliff:g id="MODENAME">%1$s</xliff:g> está ativado"</string> <string name="wallet_title" msgid="5369767670735827105">"Carteira"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Prepare tudo para fazer compras mais rápidas e seguras com seu smartphone"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue usando o teclado e o touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Deslize para a esquerda ou direita com 3 dedos no touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Legal!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Você concluiu o gesto para voltar."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir para a página inicial"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Deslize para cima com 3 dedos no touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Muito bem!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Você concluiu o gesto para acessar a tela inicial"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver os apps recentes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Deslize para cima e pressione com 3 dedos no touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string> + <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> <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 cca6d7234a21..04d3ec5c984b 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Não definido"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerir nas definições"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nenhum modo ativo}=1{{mode} está ativo}many{# modos estão ativos}other{# modos estão ativos}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Não é incomodado por sons e vibrações, exceto de alarmes, lembretes, eventos e autores de chamadas que especificar. Continua a ouvir tudo o que optar por reproduzir, incluindo música, vídeos e jogos."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com toque desativado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível porque Não incomodar está ativado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com Não incomodar ativado"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para reativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para ativar a vibração. Os serviços de acessibilidade podem ser silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para desativar o som. Os serviços de acessibilidade podem ser silenciados."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue com o teclado e o touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos de teclado e muito mais"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Aceder ao ecrã principal"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recentes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluir"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Deslize rapidamente para a esquerda ou direita com 3 dedos no touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Boa!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Concluiu o gesto para retroceder."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Aceder ao ecrã principal"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Deslize para cima com 3 dedos no touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"É assim mesmo!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Concluiu o gesto para aceder ao ecrã principal"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recentes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Deslize rapidamente para cima e mantenha premido com 3 dedos no touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Concluiu o gesto para ver as apps recentes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas as apps"</string> + <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="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 b27242248da2..c34f2f72a53d 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Ativado"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Ativado • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Desativado"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Não definido"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gerenciar nas configurações"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Nenhum modo ativo}=1{{mode} está ativo}one{# modo está ativo}many{# de modos estão ativos}other{# modos estão ativos}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Você não será perturbado por sons e vibrações, exceto alarmes, lembretes, eventos e chamadas de pessoas especificadas. No entanto, você ouvirá tudo o que decidir reproduzir, como músicas, vídeos e jogos."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponível com o toque silenciado"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponível com o Não perturbe ativado"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponível com o Não perturbe ativado"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Toque para ativar o som."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Toque para configurar para vibrar. É possível que os serviços de acessibilidade sejam silenciados."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Toque para silenciar. É possível que os serviços de acessibilidade sejam silenciados."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Mostrar modo de demonstração"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarme"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"O modo <xliff:g id="MODENAME">%1$s</xliff:g> está ativado"</string> <string name="wallet_title" msgid="5369767670735827105">"Carteira"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Prepare tudo para fazer compras mais rápidas e seguras com seu smartphone"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Aprenda gestos do touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navegue usando o teclado e o touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprenda gestos do touchpad, atalhos do teclado e muito mais"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Voltar"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir para a página inicial"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver os apps recentes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Concluído"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Voltar"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Deslize para a esquerda ou direita com 3 dedos no touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Legal!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Você concluiu o gesto para voltar."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir para a página inicial"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Deslize para cima com 3 dedos no touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Muito bem!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Você concluiu o gesto para acessar a tela inicial"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver os apps recentes"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Deslize para cima e pressione com 3 dedos no touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Muito bem!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Você concluiu o gesto para ver os apps recentes."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todos os apps"</string> + <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> <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 4222b3d9542a..ba2802601df5 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Activat"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Activat • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Dezactivat"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nesetat"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Gestionează în setări"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Niciun mod activ}=1{{mode} este activ}few{# moduri sunt active}other{# de moduri sunt active}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de tine. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Indisponibil; soneria este dezactivată"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Indisponibil; funcția Nu deranja e activată"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Indisponibil; funcția Nu deranja e activată"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Învață gesturi pentru touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navighează folosind tastatura și touchpadul"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Învață gesturi pentru touchpad, comenzi rapide de la tastatură și altele"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Înapoi"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Înapoi la pagina de pornire"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Vezi aplicațiile recente"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Gata"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Înapoi"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Glisează la stânga sau la dreapta cu trei degete pe touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Bravo!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ai finalizat gestul Înapoi."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Înapoi la pagina de pornire"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Glisează în sus cu trei degete oriunde pe touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelent!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ai finalizat gestul „accesează ecranul de pornire”"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Vezi aplicațiile recente"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Glisează în sus și ține apăsat cu trei degete pe touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Excelent!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Ai finalizat gestul pentru afișarea aplicațiilor recente."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Vezi toate aplicațiile"</string> + <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> <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 0340a1e563f9..bd5a5a15726a 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Не задано"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Открыть настройки"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Включено 0 режимов}=1{Включен режим \"{mode}\"}one{Включен # режим}few{Включено # режима}many{Включено # режимов}other{Включено # режима}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Вас не будут отвлекать звуки и вибрация, за исключением сигналов будильника, напоминаний, уведомлений о мероприятиях и звонков от помеченных контактов. Вы по-прежнему будете слышать включенную вами музыку, видео, игры и т. д."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно в беззвучном режиме"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно при включенном режиме \"Не беспокоить\"."</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно при включенном режиме \"Не беспокоить\"."</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Нажмите, чтобы включить звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Нажмите, чтобы включить вибрацию. Специальные возможности могут прекратить работу."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Нажмите, чтобы выключить звук. Специальные возможности могут прекратить работу."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Узнайте о жестах на сенсорной панели."</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Навигация с помощью клавиатуры и сенсорной панели"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Узнайте о жестах на сенсорной панели, сочетаниях клавиш и многом другом."</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главную"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Жест \"Просмотр недавних приложений\""</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Проведите тремя пальцами влево или вправо по сенсорной панели."</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Отлично!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы выполнили жест для перехода назад."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На главный экран"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Проведите тремя пальцами вверх по сенсорной панели."</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отличная работа!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Вы выполнили жест для перехода на главный экран."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Просмотр недавних приложений"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Проведите вверх по сенсорной панели тремя пальцами и удерживайте."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Отлично!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Вы выполнили жест для просмотра недавних приложений."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Все приложения"</string> + <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="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 93f72584eb2e..6d2b0be529d2 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"සකසා නැත"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"සැකසීම් තුළ කළමනාකරණය කරන්න"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{සක්රිය ප්රකාර නොමැත}=1{{mode} සක්රියයි}one{ප්රකාර #ක් සක්රියයි}other{ප්රකාර #ක් සක්රියයි}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"එලාම සිහිකැඳවීම්, සිදුවීම්, සහ ඔබ සඳහන් කළ අමතන්නන් හැර, ශබ්ද සහ කම්පනවලින් ඔබට බාධා නොවනු ඇත. සංගීතය, වීඩියෝ, සහ ක්රීඩා ඇතුළු ඔබ වාදනය කිරීමට තෝරන ලද සියලු දේ ඔබට තවම ඇසෙනු ඇත."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"නාදය නිහඬ කර ඇති නිසා නොලැබේ"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"බාධා නොකරන්න ක්රියාත්මකව ඇති බැවින් ලද නොහැක"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"බාධා නොකරන්න ක්රියාත්මකව ඇති බැවින් ලද නොහැක"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. නිහඬ කිරීම ඉවත් කිරීමට තට්ටු කරන්න."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. කම්පනය කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. නිහඬ කිරීමට තට්ටු කරන්න. ප්රවේශ්යතා සේවා නිහඬ කළ හැකිය."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"ආදර්ශන ප්රකාරය පෙන්වන්න"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"එලාමය"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> ක්රියාත්මකයි"</string> <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"ඔබගේ දුරකථනය සමඟ වඩා වේගවත්, වඩා සුරක්ෂිත මිලදී ගැනීම් සිදු කිරීමට සූදානම් වන්න"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"සියල්ල පෙන්වන්න"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ස්පර්ශක පුවරු අභිනයන් ඉගෙන ගන්න"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ඔබේ යතුරු පුවරුව සහ ස්පර්ශ පෑඩ් භාවිතයෙන් සංචාලනය කරන්න"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ස්පර්ශ පෑඩ් අභිනයන්, යතුරුපුවරු කෙටිමං සහ තවත් දේ ඉගෙන ගන්න"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ආපසු යන්න"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"මුල් පිටුවට යන්න"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"මෑත යෙදුම් බලන්න"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"නිමයි"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ආපස්සට යන්න"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ඔබේ ස්පර්ශ පුවරුව මත ඇඟිලි තුනක් භාවිතයෙන් වමට හෝ දකුණට ස්වයිප් කරන්න"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"කදිමයි!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"ඔබ ආපසු යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"මුල් පිටුවට යන්න"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ඔබේ ස්පර්ශ පුවරුවේ ඇඟිලි තුනකින් ඉහළට ස්වයිප් කරන්න"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"අනර්ඝ වැඩක්!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ඔබ මුල් පිටුවට යාමේ ඉංගිතය සම්පූර්ණ කරන ලදි"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"මෑත යෙදුම් බලන්න"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ඉහළට ස්වයිප් කර ඔබේ ස්පර්ශ පුවරුව මත ඇඟිලි තුනක් භාවිත කර රඳවාගෙන සිටින්න"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"අනර්ඝ වැඩක්!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"ඔබ මෑත යෙදුම් ඉංගිත බැලීම සම්පූර්ණ කර ඇත."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"සියලු යෙදුම් බලන්න"</string> + <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="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 e7c92634670f..223070c5846b 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Zapnuté"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Zapnuté • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Vypnuté"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Nenastavené"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Správa v nastaveniach"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Žiadne aktívne režimy}=1{{mode} je aktívny}few{# režimy sú aktívne}many{# modes are active}other{# režimov je aktívnych}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Nebudú vás vyrušovať zvuky ani vibrácie, iba budíky, pripomenutia, udalosti a volajúci, ktorých určíte. Budete naďalej počuť všetko, čo sa rozhodnete prehrať, ako napríklad hudbu, videá a hry."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nedostupné, pretože je vypnuté zvonenie"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nedostupné, pretože je zapnutý režim bez vyrušení"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nedostupné, zapnutý režim bez vyrušení"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Klepnutím zapnite zvuk."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Klepnutím aktivujte režim vibrovania. Služby dostupnosti je možné stlmiť."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Klepnutím vypnite zvuk. Služby dostupnosti je možné stlmiť."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučte sa gestá touchpadu"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Prechádzajte pomocou klávesnice a touchpadu"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Naučte sa gestá touchpadu, klávesové skratky a ďalšie funkcie"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Prejsť späť"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Prejsť na plochu"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Zobrazenie nedávnych aplikácií"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Hotovo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Prejsť späť"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Potiahnite troma prstami na touchpade doľava alebo doprava"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Výborne!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dokončili ste gesto na prechod späť."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Prechod na plochu"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Potiahnite troma prstami na touchpade nahor"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Skvelé!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dokončili ste gesto na prechod na plochu"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazenie nedávnych aplikácií"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Potiahnite troma prstami na touchpade nahor a pridržte"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Skvelé!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Dokončili ste gesto na zobrazenie nedávnych aplikácií."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazenie všetkých aplikácií"</string> + <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">"Výborne!"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dokončili ste gesto na zobrazenie všetkých aplikácií"</string> <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 701cdd1f1bfe..d44e70ccbf2e 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ni na voljo, ker je zvonjenje izklopljeno"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ni na voljo, ker je vklopljen način »Ne moti«"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ni na voljo, ker je vklopljen način »Ne moti«"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dotaknite se, če želite vklopiti zvok."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dotaknite se, če želite nastaviti vibriranje. V storitvah za dostopnost bo morda izklopljen zvok."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dotaknite se, če želite izklopiti zvok. V storitvah za dostopnost bo morda izklopljen zvok."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 20f7e822a139..8787a60e1907 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Nuk ofrohet; ziles i është hequr zëri"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Nuk ofrohet; \"Mos shqetëso\" është aktiv"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Nuk ofrohet; \"Mos shqetëso\" është aktiv"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Trokit për të aktivizuar."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Trokit për ta caktuar te dridhja. Shërbimet e qasshmërisë mund të çaktivizohen."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Trokit për të çaktivizuar. Shërbimet e qasshmërisë mund të çaktivizohen."</string> @@ -706,8 +710,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Shfaq modalitetin e demonstrimit"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Eternet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarmi"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"\"<xliff:g id="MODENAME">%1$s</xliff:g>\" është aktiv"</string> <string name="wallet_title" msgid="5369767670735827105">"Portofoli"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Konfiguro për të kryer pagesa më të shpejta dhe më të sigurta përmes telefonit"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Shfaqi të gjitha"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index f757cdcf1a6d..599d71a685ea 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Није подешено"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Управљајте у подешавањима"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Нема активних режима}=1{Активан је {mode} режим}one{Активан је # режим}few{Активна су # режима}other{Активно је # режима}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Неће вас узнемиравати звукови и вибрације осим за аларме, подсетнике, догађаје и позиваоце које наведете. И даље ћете чути све што одаберете да пустите, укључујући музику, видео снимке и игре."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно јер је звук искључен"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно јер је укључен режим Не узнемиравај"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно јер је укључен режим Не узнемиравај"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Додирните да бисте укључили звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Додирните да бисте подесили на вибрацију. Звук услуга приступачности ће можда бити искључен."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Додирните да бисте искључили звук. Звук услуга приступачности ће можда бити искључен."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Научите покрете за тачпед"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Крећите се помоћу тастатуре и тачпeда"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Научите покрете за тачпед, тастерске пречице и друго"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Назад"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Иди на почетни екран"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи недавно коришћене апликације"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Превуците улево или удесно са три прста на тачпеду"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Свака част!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Довршили сте покрет за повратак."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Иди на почетни екран"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Превуците нагоре са три прста на тачпеду"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Довршили сте покрет за повратак на почетну страницу."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прикажи недавно коришћене апликације"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Превуците нагоре и задржите са три прста на тачпеду"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Довршили сте покрет за приказивање недавно коришћених апликација."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Прикажи све апликације"</string> + <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="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 c8ece1313c49..e1d13316b115 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -672,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Otillgängligt eftersom ringljudet är av"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Inte tillgängligt eftersom Stör ej är aktiverat"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Inte tillgängligt eftersom Stör ej är aktiverat"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Tryck här om du vill slå på ljudet."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tryck här om du vill sätta på vibrationen. Tillgänglighetstjänster kanske inaktiveras."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Tryck här om du vill stänga av ljudet. Tillgänglighetstjänsterna kanske inaktiveras."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 17a88239b135..d7ffdd458f09 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Imewashwa"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Imewashwa • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Imezimwa"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Haijawekwa"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Dhibiti katika mipangilio"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Hakuna hali zinazotumika}=1{Unatumia {mode}}other{Unatumia hali #}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Hutasumbuliwa na sauti na mitetemo, isipokuwa kengele, vikumbusho, matukio na simu zinazopigwa na watu uliobainisha. Bado utasikia chochote utakachochagua kucheza, ikiwa ni pamoja na muziki, video na michezo."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Halipatikani kwa sababu sauti imezimwa"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Imeshindwa kwa sababu Usinisumbue imewashwa"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Gusa ili urejeshe."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Gusa ili uweke mtetemo. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Gusa ili ukomeshe. Huenda ikakomesha huduma za zana za walio na matatizo ya kuona au kusikia."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Jifunze kuhusu miguso ya padi ya kugusa"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Kusogeza kwa kutumia kibodi na padi yako ya kugusa"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Jifunze kuhusu miguso ya padi ya kugusa, mikato ya kibodi na mengineyo"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Rudi nyuma"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Nenda kwenye ukurasa wa mwanzo"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Angalia programu za hivi majuzi"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Nimemaliza"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Rudi nyuma"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Telezesha vidole vitatu kushoto au kulia kwenye padi yako ya kugusa"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Safi!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Umekamilisha ishara ya kurudi nyuma."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Nenda kwenye skrini ya kwanza"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Telezesha vidole vitatu juu kwenye padi yako ya kugusa"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Kazi nzuri!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Umeweka ishara ya kwenda kwenye skrini ya kwanza"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Angalia programu za hivi majuzi"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Telezesha vidole vitatu juu kisha ushikilie kwenye padi yako ya kugusa"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Kazi nzuri!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Umekamilisha mafunzo ya mguso wa kuangalia programu za hivi majuzi."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Angalia programu zote"</string> + <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> <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-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml index fc6d20e11d3b..c661846d025f 100644 --- a/packages/SystemUI/res/values-sw600dp-land/config.xml +++ b/packages/SystemUI/res/values-sw600dp-land/config.xml @@ -27,6 +27,12 @@ <!-- Whether to use the split 2-column notification shade --> <bool name="config_use_split_notification_shade">true</bool> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">3</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">2</integer> + <!-- The number of columns in the QuickSettings --> <integer name="quick_settings_num_columns">2</integer> diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml index 7daad1a43f73..f556b97eefc2 100644 --- a/packages/SystemUI/res/values-sw600dp-port/config.xml +++ b/packages/SystemUI/res/values-sw600dp-port/config.xml @@ -21,6 +21,12 @@ <!-- The maximum number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">3</integer> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">3</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">2</integer> + <!-- The number of columns in the QuickSettings --> <integer name="quick_settings_num_columns">3</integer> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 2381e1b5cc0b..ba1b0ec52aec 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"\'ரிங்\' மியூட்டில் உள்ளதால் கிடைக்கவில்லை"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"\'தொந்தரவு செய்ய வேண்டாம்\' ஆனில் இருப்பதால் இல்லை"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"\'தொந்தரவு செய்ய வேண்டாம்\' ஆனில் இருப்பதால் இல்லை"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. ஒலி இயக்க, தட்டவும்."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. அதிர்விற்கு அமைக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. ஒலியடக்க, தட்டவும். அணுகல்தன்மை சேவைகள் ஒலியடக்கப்படக்கூடும்."</string> @@ -972,7 +976,7 @@ <string name="notification_channel_screenshot" msgid="7665814998932211997">"ஸ்கிரீன் ஷாட்டுகள்"</string> <string name="notification_channel_instant" msgid="7556135423486752680">"இன்ஸ்டண்ட் ஆப்ஸ்"</string> <string name="notification_channel_setup" msgid="7660580986090760350">"அமைவு"</string> - <string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பிடம்"</string> + <string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பகம்"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"குறிப்புகள்"</string> <string name="notification_channel_accessibility" msgid="8956203986976245820">"மாற்றுத்திறன் வசதி"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index fcc344bc84c6..8eeb1490b3ce 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"సెట్ చేసి లేదు"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"సెట్టింగ్లలో మేనేజ్ చేయండి"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{మోడ్స్ ఏవీ యాక్టివ్గా లేవు}=1{{mode} యాక్టివ్గా ఉంది}other{# మోడ్స్ యాక్టివ్గా ఉన్నాయి}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"మీరు పేర్కొనే అలారాలు, రిమైండర్లు, ఈవెంట్లు మరియు కాలర్ల నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"వాల్యూమ్ మ్యూట్ అయినందున అందుబాటులో లేదు"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"అంతరాయంకలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"అంతరాయంకలిగించవద్దు ఆన్లో ఉన్నందున అందుబాటులోలేదు"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. అన్మ్యూట్ చేయడానికి నొక్కండి."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. వైబ్రేషన్కు సెట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. మ్యూట్ చేయడానికి నొక్కండి. యాక్సెస్ సామర్థ్య సేవలు మ్యూట్ చేయబడవచ్చు."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"టచ్ప్యాడ్ సంజ్ఞ గురించి తెలుసుకోండి"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"మీ కీబోర్డ్, టచ్ప్యాడ్ను ఉపయోగించి నావిగేట్ చేయండి"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"టచ్ప్యాడ్ సంజ్ఞలు, కీబోర్డ్ షార్ట్కట్లు, అలాగే మరిన్నింటిని గురించి తెలుసుకోండి"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"వెనుకకు వెళ్లండి"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"మొదటి ట్యాబ్కు వెళ్లండి"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ఇటీవలి యాప్లను చూడండి"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"పూర్తయింది"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"వెనుకకు"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"మీ టచ్ప్యాడ్లో మూడు వేళ్లను ఉపయోగించి ఎడమ వైపునకు లేదా కుడి వైపునకు స్వైప్ చేయండి"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"పనితీరు బాగుంది!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"తిరిగి వెనుకకు వెళ్ళడానికి ఉపయోగించే సంజ్ఞకు సంబంధించిన ట్యుటోరియల్ను మీరు పూర్తి చేశారు."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"మొదటి ట్యాబ్కు వెళ్లండి"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"మీ టచ్ప్యాడ్పై మూడు వేళ్లతో పైకి స్వైప్ చేయండి"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"చక్కగా పూర్తి చేశారు!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"మీరు మొదటి స్క్రీన్కు వెళ్లే సంజ్ఞను పూర్తి చేశారు"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ఇటీవలి యాప్లను చూడండి"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"మీ టచ్ప్యాడ్లో మూడు వేళ్లను ఉపయోగించి పైకి స్వైప్ చేసి, హోల్డ్ చేయండి"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"పనితీరు అద్భుతంగా ఉంది!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"మీరు ఇటీవలి యాప్ల వీక్షణ సంజ్ఞను పూర్తి చేశారు."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"అన్ని యాప్లను చూడండి"</string> + <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="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 06c26619bb86..44a33ecaeb0c 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"ไม่ได้ตั้งค่า"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"จัดการในการตั้งค่า"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{ไม่มีโหมดที่ใช้งานอยู่}=1{ใช้งานอยู่ {mode} โหมด}other{ใช้งานอยู่ # โหมด}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"คุณจะไม่ถูกรบกวนจากเสียงและการสั่น ยกเว้นเสียงนาฬิกาปลุก การช่วยเตือน กิจกรรม และผู้โทรที่ระบุไว้ คุณจะยังคงได้ยินสิ่งที่คุณเลือกเล่น เช่น เพลง วิดีโอ และเกม"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"เปลี่ยนไม่ได้เนื่องจากปิดเสียงเรียกเข้า"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"ไม่พร้อมใช้งานเนื่องจากโหมดห้ามรบกวนเปิดอยู่"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s แตะเพื่อเปิดเสียง"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s แตะเพื่อตั้งค่าให้สั่น อาจมีการปิดเสียงบริการการเข้าถึง"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s แตะเพื่อปิดเสียง อาจมีการปิดเสียงบริการการเข้าถึง"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"ไปยังส่วนต่างๆ โดยใช้แป้นพิมพ์และทัชแพด"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ดูข้อมูลเกี่ยวกับท่าทางสัมผัสของทัชแพด แป้นพิมพ์ลัด และอื่นๆ"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"ย้อนกลับ"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"ไปที่หน้าแรก"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"ดูแอปล่าสุด"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"เสร็จสิ้น"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาบนทัชแพด"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ดีมาก"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ไปที่หน้าแรก"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"เก่งมาก"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ดูแอปล่าสุด"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"เยี่ยมมาก"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"คุณทำท่าทางสัมผัสเพื่อดูแอปล่าสุดสำเร็จแล้ว"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"ดูแอปทั้งหมด"</string> + <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="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 8241b723ae3c..c782dd412196 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Naka-on"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Naka-on • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Naka-off"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Hindi nakatakda"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Pamahalaan sa mga setting"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Walang aktibong mode}=1{Aktibo ang {mode}}one{# mode ang aktibo}other{# na mode ang aktibo}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Hindi ka maiistorbo ng mga tunog at pag-vibrate, maliban mula sa mga alarm, paalala, event, at tumatawag na tutukuyin mo. Maririnig mo pa rin ang kahit na anong piliin mong i-play kabilang ang mga musika, video, at laro."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Hindi available dahil naka-mute ang ring"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Hindi available dahil naka-on ang Huwag Istorbohin"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Hindi available dahil naka-on ang Huwag Istorbohin"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. I-tap upang i-unmute."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. I-tap upang itakda na mag-vibrate. Maaaring i-mute ang mga serbisyo sa Accessibility."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. I-tap upang i-mute. Maaaring i-mute ang mga serbisyo sa Accessibility."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Matuto ng mga galaw sa touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Mag-navigate gamit ang iyong keyboard at touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Matuto ng mga galaw sa touchpad, keyboard shortcut, at higit pa"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Bumalik"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pumunta sa home"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Tingnan ang mga kamakailang app"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tapos na"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Bumalik"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Mag-swipe pakaliwa o pakanan gamit ang tatlong daliri sa iyong touchpad"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Magaling!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Nakumpleto mo na ang galaw para bumalik."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Pumunta sa home"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Mag-swipe pataas gamit ang tatlong daliri sa iyong touchpad"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Magaling!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Nakumpleto mo na ang galaw para pumunta sa home"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Tingnan ang mga kamakailang app"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Mag-swipe pataas at i-hold gamit ang tatlong daliri sa iyong touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Magaling!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Nakumpleto mo ang galaw sa pag-view ng mga kamakailang app."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Tingnan ang lahat ng app"</string> + <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> <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 966c6efd942e..ad543cfc72ce 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Zil sesi kapatıldığı için kullanılamıyor"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Rahatsız Etmeyin açık olduğu için kullanılamıyor"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Rahatsız Etmeyin açık olduğu için kullanılamıyor"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Sesi açmak için dokunun."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Titreşime ayarlamak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Sesi kapatmak için dokunun. Erişilebilirlik hizmetlerinin sesi kapatılabilir."</string> @@ -706,8 +710,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Demo modunu göster"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Alarm"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"<xliff:g id="MODENAME">%1$s</xliff:g> açık"</string> <string name="wallet_title" msgid="5369767670735827105">"Cüzdan"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Telefonunuzla daha hızlı ve güvenli satın alma işlemleri gerçekleştirmek için gerekli ayarları yapın"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Tümünü göster"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index c146021968ae..0d664e3f16bb 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -673,6 +673,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Недоступно: звук дзвінків вимкнено"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Недоступно: увімкнено режим \"Не турбувати\""</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Недоступно: увімкнено режим \"Не турбувати\""</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Торкніться, щоб увімкнути звук."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Торкніться, щоб налаштувати вібросигнал. Спеціальні можливості може бути вимкнено."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Торкніться, щоб вимкнути звук. Спеціальні можливості може бути вимкнено."</string> @@ -706,8 +710,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Показати демонстраційний режим"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"Будильник"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"Увімкнено режим \"<xliff:g id="MODENAME">%1$s</xliff:g>\""</string> <string name="wallet_title" msgid="5369767670735827105">"Гаманець"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Швидше й безпечніше сплачуйте за покупки за допомогою телефона"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Показати все"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index a0e934749b3a..b1ddfc98d308 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"سیٹ نہیں ہے"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ترتیبات میں نظم کریں"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{کوئی فعال موڈ نہیں ہے}=1{{mode} فعال ہے}other{# موڈز فعال ہیں}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"الارمز، یاددہانیوں، ایونٹس اور آپ کے متعین کردہ کالرز کے علاوہ، آپ آوازوں اور وائبریشنز سے ڈسٹرب نہیں ہوں گے۔ موسیقی، ویڈیوز اور گیمز سمیت آپ ابھی بھی ہر وہ چیز سنیں گے جسے چلانے کا آپ انتخاب کرتے ہیں۔"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"دستیاب نہیں ہے کیونکہ رنگ خاموش ہے"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"غیر دستیاب ہے کیونکہ ڈسٹرب نہ کریں آن ہے"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"غیر دستیاب ہے کیونکہ ڈسٹرب نہ کریں آن ہے"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s۔ آواز چالو کرنے کیلئے تھپتھپائیں۔"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s۔ ارتعاش پر سیٹ کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s۔ خاموش کرنے کیلئے تھپتھپائیں۔ ایکسیسبیلٹی سروسز شاید خاموش ہوں۔"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"ٹچ پیڈ کے اشارے کو جانیں"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"اپنے کی بورڈ اور ٹچ پیڈ کا استعمال کر کے نیویگیٹ کریں"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"ٹچ پیڈ کے اشارے، کی بورڈ شارٹ کٹس اور مزید جانیں"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"واپس جائیں"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"گھر جائیں"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"حالیہ ایپس دیکھیں"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"ہو گیا"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"واپس جائیں"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے دائیں یا بائیں طرف سوائپ کریں"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"عمدہ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"آپ نے واپس جائیں اشارے کو مکمل کر لیا۔"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ہوم پر جائیں"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"اپنے ٹچ پیڈ پر تین انگلیوں کی مدد سے اوپر کی طرف سوائپ کریں"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"بہترین!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"آپ نے ہوم پر جانے کا اشارہ مکمل کر لیا"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"حالیہ ایپس دیکھیں"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے اوپر کی طرف سوائپ کریں اور دبائے رکھیں"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"بہترین!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس کا اشارہ مکمل کر لیا ہے۔"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"سبھی ایپس دیکھیں"</string> + <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="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 05943136ee3a..6d597bd99609 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Yoniq"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Yoniq • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Yoqilmagan"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Sozlanmagan"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Sozlamalarda boshqarish"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{0 ta rejim faol}=1{{mode} faol}other{# ta rejim faol}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Turli ovoz va tebranishlar endi sizni bezovta qilmaydi. Biroq, signallar, eslatmalar, tadbirlar haqidagi bildirishnomalar va siz tanlagan abonentlardan kelgan chaqiruvlar bundan mustasno. Lekin, ijro etiladigan barcha narsalar, jumladan, musiqa, video va o‘yinlar ovozi eshitiladi."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Jiringlash ovozsizligi uchun ishlamaydi"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Bezovta qilinmasin yoniqligi sababli ishlamaydi"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Bezovta qilinmasin yoniqligi sababli ishlamaydi"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Ovozini yoqish uchun ustiga bosing."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Tebranishni yoqish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Ovozini o‘chirish uchun ustiga bosing. Qulayliklar ishlamasligi mumkin."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Sensorli panel ishoralari haqida"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura va sensorli panel yordamida kezing"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Orqaga"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Yaxshi!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ortga qaytish ishorasi darsini tamomladingiz."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Boshiga"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Sensorli panelda uchta barmoq bilan tepaga suring"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Barakalla!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Bosh ekranni ochish ishorasi darsini tamomladingiz"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Oxirgi ilovalarni koʻrish"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Sensorli panelda uchta barmoq bilan tepaga surib, bosib turing"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Barakalla!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Oxirgi ilovalarni koʻrish ishorasini tugalladingiz."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Barcha ilovalarni koʻrish"</string> + <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="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 c38f99fcb984..7e1f6e7780b6 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Đang bật"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Bật • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Đang tắt"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Chưa đặt"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Quản lý trong phần cài đặt"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Không có chế độ nào đang hoạt động}=1{{mode} đang hoạt động}other{# chế độ đang hoạt động}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Bạn sẽ không bị làm phiền bởi âm thanh và tiếng rung, ngoại trừ báo thức, lời nhắc, sự kiện và người gọi mà bạn chỉ định. Bạn sẽ vẫn nghe thấy mọi thứ bạn chọn phát, bao gồm nhạc, video và trò chơi."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Không hoạt động vì chuông bị tắt"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Bị tắt vì đang bật chế độ Không làm phiền"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Bị tắt vì đang bật chế độ Không làm phiền"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Nhấn để bật tiếng."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Nhấn để đặt chế độ rung. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Nhấn để tắt tiếng. Bạn có thể tắt tiếng dịch vụ trợ năng."</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Tìm hiểu về cử chỉ trên bàn di chuột"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Di chuyển bằng bàn phím và bàn di chuột"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Tìm hiểu về cử chỉ trên bàn di chuột, phím tắt và nhiều mục khác"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Quay lại"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Chuyển đến màn hình chính"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Xem các ứng dụng gần đây"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Xong"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Quay lại"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Dùng 3 ngón tay vuốt sang trái hoặc sang phải trên bàn di chuột"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Tuyệt vời!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Bạn đã thực hiện xong cử chỉ quay lại."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Chuyển đến màn hình chính"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Dùng 3 ngón tay vuốt lên trên bàn di chuột"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Tuyệt vời!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Bạn đã thực hiện xong cử chỉ chuyển đến màn hình chính"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Xem các ứng dụng gần đây"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Dùng 3 ngón tay vuốt lên và giữ trên bàn di chuột"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Tuyệt vời!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Bạn đã hoàn tất cử chỉ xem ứng dụng gần đây."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Xem tất cả các ứng dụng"</string> + <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> <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 60e6c4fcb054..0aa679821511 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"未设置"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在设置中管理"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{未启用任何模式}=1{已启用“{mode}”模式}other{已启用 # 个模式}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"您将不会受到声音和振动的打扰(闹钟、提醒、活动和所指定来电者的相关提示音除外)。您依然可以听到您选择播放的任何内容(包括音乐、视频和游戏)的相关音效。"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"该功能无法使用,因为铃声被静音"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"“勿扰”模式已开启,因此无法调整音量"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"“勿扰”模式已开启,因此无法调整音量"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。点按即可取消静音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。点按即可设为振动,但可能会同时将无障碍服务设为静音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。点按即可设为静音,但可能会同时将无障碍服务设为静音。"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"了解触控板手势"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用键盘和触控板进行导航"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"了解触控板手势、键盘快捷键等"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"前往主屏幕"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近用过的应用"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在触控板上用三根手指向左或向右滑动"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"太棒了!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您完成了“返回”手势教程。"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"前往主屏幕"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在触控板上用三根手指向上滑动"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"您已完成“前往主屏幕”手势"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近用过的应用"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在触控板上用三根手指向上滑动并按住"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"您已完成“查看最近用过的应用”的手势教程。"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string> + <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="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 a838da4c02e4..4ff574e27ef2 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"未設定"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{沒有啟用模式}=1{已啟用{mode}}other{已啟用 # 個模式}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"你不會受到聲音和震動騷擾 (鬧鐘、提醒、活動和你指定的來電者鈴聲除外)。當你選擇播放音樂、影片和遊戲等,仍可以聽到該內容的聲音。"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設定為靜音,因此無法使用"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"「請勿騷擾」已開啟,因此無法使用"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"「請勿騷擾」已開啟,因此無法使用"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕按即可取消靜音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕按即可設為震動。無障礙功能服務可能已經設為靜音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕按即可設為靜音。無障礙功能服務可能已經設為靜音。"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"瞭解觸控板手勢"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用鍵盤和觸控板導覽"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"瞭解觸控板手勢、鍵盤快速鍵等等"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在觸控板上用三隻手指向左或向右滑動"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"很好!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"你已完成「返回」手勢的教學課程。"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"返回主畫面"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在觸控板上用三隻手指向上滑動"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太好了!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"你已完成「返回主畫面」手勢的教學課程"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近使用的應用程式"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在觸控板上用三隻手指向上滑動並按住"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"做得好!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢的教學課程。"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string> + <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="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 158b68a7d9b7..9fea9bc5b9f1 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -440,8 +440,7 @@ <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> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"未設定"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"在「設定」中管理"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{未啟用任何模式}=1{已啟用 {mode} 個模式}other{已啟用 # 個模式}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"裝置不會發出音效或震動造成干擾,但是會保留與鬧鐘、提醒、活動和指定來電者有關的設定。如果你選擇播放音樂、影片和遊戲等內容,還是可以聽見相關音訊。"</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"鈴聲已設為靜音,因此無法使用"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"「零打擾」模式已開啟,因此無法調整音量"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"「零打擾」模式已開啟,因此無法調整音量"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s。輕觸即可取消靜音。"</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s。輕觸即可設為震動,但系統可能會將無障礙服務一併設為靜音。"</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s。輕觸即可設為靜音,但系統可能會將無障礙服務一併設為靜音。"</string> @@ -1406,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"學習觸控板手勢"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"使用鍵盤和觸控板操作"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"學習觸控板手勢、鍵盤快速鍵等"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"返回"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"返回主畫面"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"查看最近使用的應用程式"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"完成"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在觸控板上用三指向左或向右滑動"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"很好!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"你已完成「返回」手勢的教學課程。"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"返回主畫面"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在觸控板上用三指向上滑動"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"你已完成「返回主畫面」手勢教學課程"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"查看最近使用的應用程式"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"在觸控板上用三指向上滑動並按住"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"太棒了!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"你已完成「查看最近使用的應用程式」手勢教學課程。"</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有應用程式"</string> + <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="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 a351b1b91f64..9ff3e5dee7c6 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -440,8 +440,7 @@ <string name="zen_mode_on" msgid="9085304934016242591">"Vuliwe"</string> <string name="zen_mode_on_with_details" msgid="7416143430557895497">"Vuliwe • <xliff:g id="TRIGGER_DESCRIPTION">%1$s</xliff:g>"</string> <string name="zen_mode_off" msgid="1736604456618147306">"Valiwe"</string> - <!-- no translation found for zen_mode_set_up (8231201163894922821) --> - <skip /> + <string name="zen_mode_set_up" msgid="8231201163894922821">"Akusethiwe"</string> <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"Phatha kumasethingi"</string> <string name="zen_mode_active_modes" msgid="1625850411578488856">"{count,plural, =0{Awekho amamodi asebenzayo}=1{I-{mode} iyasebenza}one{Amamodi angu-# ayasebenza}other{Amamodi angu-# ayasebenza}}"</string> <string name="zen_priority_introduction" msgid="3159291973383796646">"Ngeke uphazanyiswe imisindo nokudlidliza, ngaphandle kusukela kuma-alamu, izikhumbuzi, imicimbi, nabafonayo obacacisayo. Usazozwa noma yini okhetha ukuyidlala okufaka umculo, amavidiyo, namageyimu."</string> @@ -673,6 +672,10 @@ <string name="stream_notification_unavailable" msgid="4313854556205836435">"Ayitholakali ngoba ukukhala kuthulisiwe"</string> <string name="stream_alarm_unavailable" msgid="4059817189292197839">"Ayitholakali ngoba okuthi Ungaphazamisi kuvuliwe"</string> <string name="stream_media_unavailable" msgid="6823020894438959853">"Ayitholakali ngoba okuthi Ungaphazamisi kuvuliwe"</string> + <!-- no translation found for stream_unavailable_by_modes (3674139029490353683) --> + <skip /> + <!-- no translation found for stream_unavailable_by_unknown (6908434629318171588) --> + <skip /> <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Thepha ukuze ususe ukuthula."</string> <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Thepha ukuze usethe ukudlidliza. Amasevisi okufinyelela angathuliswa."</string> <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Thepha ukuze uthulise. Amasevisi okufinyelela angathuliswa."</string> @@ -706,8 +709,7 @@ <string name="show_demo_mode" msgid="3677956462273059726">"Bonisa imodi yedemo"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"I-Ethernet"</string> <string name="status_bar_alarm" msgid="87160847643623352">"I-alamu"</string> - <!-- no translation found for active_mode_content_description (1627555562186515927) --> - <skip /> + <string name="active_mode_content_description" msgid="1627555562186515927">"I-<xliff:g id="MODENAME">%1$s</xliff:g> ivuliwe"</string> <string name="wallet_title" msgid="5369767670735827105">"I-wallet"</string> <string name="wallet_empty_state_label" msgid="7776761245237530394">"Lungela ukuthenga ngokushesha, ngokuphepha ngefoni yakho"</string> <string name="wallet_app_button_label" msgid="7123784239111190992">"Bonisa konke"</string> @@ -1407,38 +1409,26 @@ <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Funda ukunyakaza kwephedi lokuthinta"</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Funa usebenzisa ikhibhodi yakho nephedi yokuthinta"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Funda ukunyakaza kwephedi yokuthinta, izinqamuleli zamakhibhodi, nokuningi"</string> - <!-- no translation found for touchpad_tutorial_back_gesture_button (3104716365403620315) --> - <skip /> - <!-- no translation found for touchpad_tutorial_home_gesture_button (8023973153559885624) --> - <skip /> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Iya emuva"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Iya ekhasini lokuqala"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Buka ama-app akamuva"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Kwenziwe"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Buyela emuva"</string> - <!-- no translation found for touchpad_back_gesture_guidance (5352221087725906542) --> - <skip /> - <!-- no translation found for touchpad_back_gesture_success_title (7370719098633023496) --> - <skip /> + <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Swayiphela kwesokunxele noma kwesokudla usebenzisa iminwe emithathu kuphedi yokuthinta"</string> + <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Kuhle!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ukuqedile ukuthinta kokubuyela emuva."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Iya ekhasini lokuqala"</string> - <!-- no translation found for touchpad_home_gesture_guidance (4178219118381915899) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_title (3648264553645798470) --> - <skip /> - <!-- no translation found for touchpad_home_gesture_success_body (2590690589194027059) --> - <skip /> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swayiphela phezulu ngeminwe emithathu ephedini yakho yokuthinta"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Umsebenzi omuhle!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ukuqedile ukunyakaza kokuya ekhaya"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Buka ama-app akamuva"</string> - <!-- no translation found for touchpad_recent_apps_gesture_guidance (6304446013842271822) --> - <skip /> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swayiphela phezulu bese ubamba usebenzisa iminwe emithathu ephedini yokuthinta."</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Umsebenzi omuhle!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Uqedele ukubuka ukuthinta kwama-app akamuva."</string> - <!-- no translation found for tutorial_action_key_title (8172535792469008169) --> - <skip /> - <!-- no translation found for tutorial_action_key_guidance (5040613427202799294) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_title (2371827347071979571) --> - <skip /> - <!-- no translation found for tutorial_action_key_success_body (1688986269491357832) --> - <skip /> + <string name="tutorial_action_key_title" msgid="8172535792469008169">"Buka wonke ama-app"</string> + <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> <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/config.xml b/packages/SystemUI/res/values/config.xml index 6f94f9e2a216..16a8bc5b034f 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -70,6 +70,12 @@ <!-- The number of rows in the QuickSettings --> <integer name="quick_settings_max_rows">4</integer> + <!-- The number of rows in the paginated grid QuickSettings --> + <integer name="quick_settings_paginated_grid_num_rows">4</integer> + + <!-- The number of rows in the paginated grid QuickQuickSettings --> + <integer name="quick_qs_paginated_grid_num_rows">2</integer> + <!-- The number of columns in the infinite grid QuickSettings --> <integer name="quick_settings_infinite_grid_num_columns">4</integer> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e1f25f99cc98..7cebac2feaba 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -70,6 +70,20 @@ <item name="android:fontWeight">700</item> </style> + <style name="StatusBar" /> + <style name="StatusBar.Chip" /> + + <style name="StatusBar.Chip.Text"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:singleLine">true</item> + <item name="android:gravity">center|start</item> + <item name="android:paddingStart">@dimen/ongoing_activity_chip_icon_text_padding</item> + <item name="android:textAppearance">@android:style/TextAppearance.Material.Small</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?android:attr/colorPrimary</item> + </style> + <style name="Chipbar" /> <style name="Chipbar.Text" parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title"> @@ -677,10 +691,12 @@ <style name="TunerSettings" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="android:windowActionBar">false</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> <item name="preferenceTheme">@style/TunerPreferenceTheme</item> </style> <style name="TunerPreferenceTheme" parent="@style/PreferenceThemeOverlay.SettingsBase"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="TextAppearance.NotificationInfo.Confirmation"> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 4a96e9e0845a..c7ea98052b66 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -193,12 +193,16 @@ public class KeyguardClockSwitch extends RelativeLayout { protected void onFinishInflate() { super.onFinishInflate(); if (!MigrateClocksToBlueprint.isEnabled()) { - mSmallClockFrame = findViewById(R.id.lockscreen_clock_view); - mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large); + mSmallClockFrame = findViewById( + com.android.systemui.customization.R.id.lockscreen_clock_view); + mLargeClockFrame = findViewById( + com.android.systemui.customization.R.id.lockscreen_clock_view_large); mStatusArea = findViewById(R.id.keyguard_status_area); } else { - removeView(findViewById(R.id.lockscreen_clock_view)); - removeView(findViewById(R.id.lockscreen_clock_view_large)); + removeView(findViewById( + com.android.systemui.customization.R.id.lockscreen_clock_view)); + removeView(findViewById( + com.android.systemui.customization.R.id.lockscreen_clock_view_large)); } onConfigChanged(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index d468f2f0b0aa..7cba845460ca 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -241,8 +241,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mKeyguardSliceViewController.init(); if (!MigrateClocksToBlueprint.isEnabled()) { - mSmallClockFrame = mView.findViewById(R.id.lockscreen_clock_view); - mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large); + mSmallClockFrame = mView + .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view); + mLargeClockFrame = mView + .findViewById(com.android.systemui.customization.R.id.lockscreen_clock_view_large); } if (!mOnlyClock) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 63a4af949c8c..0684824ea0b8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -534,7 +534,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV // KeyguardClockViewBinder if (customClockAnimation && !MigrateClocksToBlueprint.isEnabled()) { // Find the clock, so we can exclude it from this transition. - FrameLayout clockContainerView = mView.findViewById(R.id.lockscreen_clock_view_large); + FrameLayout clockContainerView = mView.findViewById( + com.android.systemui.customization.R.id.lockscreen_clock_view_large); // The clock container can sometimes be null. If it is, just fall back to the // old animation rather than setting up the custom animations. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index 19d918f5c556..5a02486d5096 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -18,6 +18,7 @@ package com.android.keyguard import android.content.Context import android.view.View +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -98,12 +99,12 @@ constructor( viewsIdToTranslate = setOf( ViewIdToTranslate( - viewId = R.id.lockscreen_clock_view_large, + viewId = customR.id.lockscreen_clock_view_large, direction = START, shouldBeAnimated = filterKeyguardAndSplitShadeOnly ), ViewIdToTranslate( - viewId = R.id.lockscreen_clock_view, + viewId = customR.id.lockscreen_clock_view, direction = START, shouldBeAnimated = filterKeyguard ), diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index a30115568842..40c1f0f9895d 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -52,7 +52,7 @@ import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.SystemUIDialogManager; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.tuner.TunerService; import dagger.Lazy; @@ -148,7 +148,7 @@ public class Dependency { @Inject Lazy<SystemUIDialogManager> mSystemUIDialogManagerLazy; @Inject Lazy<DialogTransitionAnimator> mDialogTransitionAnimatorLazy; @Inject Lazy<UserTracker> mUserTrackerLazy; - @Inject Lazy<StatusBarWindowController> mStatusBarWindowControllerLazy; + @Inject Lazy<StatusBarWindowControllerStore> mStatusBarWindowControllerStoreLazy; @Inject public Dependency() { @@ -192,7 +192,8 @@ public class Dependency { mProviders.put(SystemUIDialogManager.class, mSystemUIDialogManagerLazy::get); mProviders.put(DialogTransitionAnimator.class, mDialogTransitionAnimatorLazy::get); mProviders.put(UserTracker.class, mUserTrackerLazy::get); - mProviders.put(StatusBarWindowController.class, mStatusBarWindowControllerLazy::get); + mProviders.put( + StatusBarWindowControllerStore.class, mStatusBarWindowControllerStoreLazy::get); Dependency.setInstance(this); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt deleted file mode 100644 index 242601d46fa4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.util.AttributeSet - -/** - * Class that coordinates non-HBM animations during BiometricPrompt. - * - * Currently doesn't draw anything. - * - * Note that [AuthBiometricFingerprintViewController] also shows UDFPS animations. At some point we should - * de-dupe this if necessary. - */ -class UdfpsBpView(context: Context, attrs: AttributeSet?) : UdfpsAnimationView(context, attrs) { - - // Drawable isn't ever added to the view, so we don't currently show anything - private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context) - - override fun getDrawable(): UdfpsDrawable = fingerprintDrawable -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt deleted file mode 100644 index e0455b58b919..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager - -/** - * Class that coordinates non-HBM animations for biometric prompt. - */ -class UdfpsBpViewController( - view: UdfpsBpView, - statusBarStateController: StatusBarStateController, - shadeInteractor: ShadeInteractor, - systemUIDialogManager: SystemUIDialogManager, - dumpManager: DumpManager, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : UdfpsAnimationViewController<UdfpsBpView>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, -) { - override val tag = "UdfpsBpViewController" - - override fun shouldPauseAuth(): Boolean { - return false - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index a3904caa9dcc..2863e29c9a34 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -87,7 +87,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.ScreenLifecycle; @@ -98,7 +97,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shade.domain.interactor.ShadeInteractor; import com.android.systemui.shared.system.SysUiStatsLog; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.SystemUIDialogManager; @@ -162,7 +160,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final FalsingManager mFalsingManager; @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; - @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final SystemClock mSystemClock; @NonNull private final UnlockedScreenOffAnimationController @@ -283,7 +280,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mKeyguardUpdateMonitor, mDialogManager, mDumpManager, - mLockscreenShadeTransitionController, mConfigurationController, mKeyguardStateController, mUnlockedScreenOffAnimationController, @@ -291,10 +287,9 @@ public class UdfpsController implements DozeReceiver, Dumpable { requestId, reason, callback, - (view, event, fromUdfpsView) -> onTouch( + (view, event) -> onTouch( requestId, - event, - fromUdfpsView + event ), mActivityTransitionAnimator, mPrimaryBouncerInteractor, @@ -374,9 +369,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mOverlay == null || mOverlay.isHiding()) { return; } - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - ((UdfpsView) mOverlay.getTouchOverlay()).setDebugMessage(message); - } }); } @@ -391,7 +383,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { */ public void debugOnTouch(MotionEvent event) { final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; - UdfpsController.this.onTouch(requestId, event, true); + UdfpsController.this.onTouch(requestId, event); } /** @@ -449,22 +441,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mOverlayParams.equals(overlayParams)) { mOverlayParams = overlayParams; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) { - mOverlay.updateOverlayParams(mOverlayParams); - } else { - redrawOverlay(); - } + if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) { + mOverlay.updateOverlayParams(mOverlayParams); } else { - final boolean wasShowingAlternateBouncer = - mAlternateBouncerInteractor.isVisibleState(); - // When the bounds change it's always to re-create the overlay's window with new - // LayoutParams. If the overlay needs to be shown, this will re-create and show the - // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden. redrawOverlay(); - if (wasShowingAlternateBouncer) { - mKeyguardViewManager.showBouncer(true); - } } } } @@ -563,11 +543,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } } - private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { - if (!fromUdfpsView) { - Log.e(TAG, "ignoring the touch injected from outside of UdfpsView"); - return false; - } + private boolean onTouch(long requestId, @NonNull MotionEvent event) { if (mOverlay == null) { Log.w(TAG, "ignoring onTouch with null overlay"); return false; @@ -591,13 +567,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!mIsAodInterruptActive) { mOnFingerDown = false; } - } else if (!DeviceEntryUdfpsRefactor.isEnabled()) { - if ((mLockscreenShadeTransitionController.getQSDragProgress() != 0f - && !mAlternateBouncerInteractor.isVisibleState()) - || mPrimaryBouncerInteractor.isInTransit()) { - Log.w(TAG, "ignoring touch due to qsDragProcess or primaryBouncerInteractor"); - return false; - } } final TouchProcessorResult result = mTouchProcessor.processTouch(event, mActivePointerId, @@ -661,22 +630,13 @@ public class UdfpsController implements DozeReceiver, Dumpable { mStatusBarStateController.isDozing()); break; - case UNCHANGED: - if (mActivePointerId == MotionEvent.INVALID_POINTER_ID - && mAlternateBouncerInteractor.isVisibleState()) { - // No pointer on sensor, forward to keyguard if alternateBouncer is visible - mKeyguardViewManager.onTouch(event); - } - default: break; } logBiometricTouch(processedTouch.getEvent(), data); // Always pilfer pointers that are within sensor area or when alternate bouncer is showing - if (mActivePointerId != MotionEvent.INVALID_POINTER_ID - || (mAlternateBouncerInteractor.isVisibleState() - && !DeviceEntryUdfpsRefactor.isEnabled())) { + if (mActivePointerId != MotionEvent.INVALID_POINTER_ID) { shouldPilfer = true; } @@ -692,14 +652,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { } private boolean shouldTryToDismissKeyguard() { - boolean onKeyguard = false; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - onKeyguard = mKeyguardStateController.isShowing(); - } else { - onKeyguard = mOverlay != null - && mOverlay.getAnimationViewController() - instanceof UdfpsKeyguardViewControllerLegacy; - } + boolean onKeyguard = mKeyguardStateController.isShowing(); return onKeyguard && mKeyguardStateController.canDismissLockScreen() && !mAttemptedToDismissKeyguard; @@ -719,7 +672,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, - @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController, @NonNull ScreenLifecycle screenLifecycle, @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @@ -769,7 +721,6 @@ public class UdfpsController implements DozeReceiver, Dumpable { mFalsingManager = falsingManager; mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; - mLockscreenShadeTransitionController = lockscreenShadeTransitionController; screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mConfigurationController = configurationController; @@ -849,13 +800,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @Override public void dozeTimeTick() { - if (mOverlay != null && mOverlay.getTouchOverlay() instanceof UdfpsView) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - final View view = mOverlay.getTouchOverlay(); - if (view != null) { - ((UdfpsView) view).dozeTimeTick(); - } - } + } private void redrawOverlay() { @@ -915,17 +860,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (!isOptical()) { return; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mUdfpsDisplayMode != null) { - mUdfpsDisplayMode.disable(null); - } - } else { - if (view != null) { - UdfpsView udfpsView = (UdfpsView) view; - if (udfpsView.isDisplayConfigured()) { - udfpsView.unconfigureDisplay(); - } - } + if (mUdfpsDisplayMode != null) { + mUdfpsDisplayMode.disable(null); } } @@ -1118,11 +1054,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { if (mIgnoreRefreshRate) { dispatchOnUiReady(requestId); } else { - if (DeviceEntryUdfpsRefactor.isEnabled()) { mUdfpsDisplayMode.enable(() -> dispatchOnUiReady(requestId)); - } else { - ((UdfpsView) view).configureDisplay(() -> dispatchOnUiReady(requestId)); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 1bac0bc26a94..a1efc196dbee 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -23,8 +23,6 @@ import android.graphics.PixelFormat import android.graphics.Rect import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_FIND_SENSOR import android.hardware.biometrics.BiometricRequestConstants.RequestReason @@ -42,7 +40,6 @@ import android.view.View import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener -import androidx.annotation.LayoutRes import androidx.annotation.VisibleForTesting import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor @@ -56,7 +53,6 @@ import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlay import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState @@ -64,7 +60,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController @@ -102,7 +97,6 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val dialogManager: SystemUIDialogManager, private val dumpManager: DumpManager, - private val transitionController: LockscreenShadeTransitionController, private val configurationController: ConfigurationController, private val keyguardStateController: KeyguardStateController, private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, @@ -110,7 +104,7 @@ constructor( val requestId: Long, @RequestReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, - private val onTouch: (View, MotionEvent, Boolean) -> Boolean, + private val onTouch: (View, MotionEvent) -> Boolean, private val activityTransitionAnimator: ActivityTransitionAnimator, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, @@ -133,23 +127,15 @@ constructor( .map {} // map to Unit private var listenForCurrentKeyguardState: Job? = null private var addViewRunnable: Runnable? = null - private var overlayViewLegacy: UdfpsView? = null - private set - private var overlayTouchView: UdfpsTouchOverlay? = null /** - * Get the current UDFPS overlay touch view which is a different View depending on whether the - * DeviceEntryUdfpsRefactor flag is enabled or not. + * Get the current UDFPS overlay touch view * * @return The view, when [isShowing], else null */ fun getTouchOverlay(): View? { - return if (DeviceEntryUdfpsRefactor.isEnabled) { - overlayTouchView - } else { - overlayViewLegacy - } + return overlayTouchView } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() @@ -161,7 +147,7 @@ constructor( WindowManager.LayoutParams( WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 0 /* flags set in computeLayoutParams() */, - PixelFormat.TRANSLUCENT + PixelFormat.TRANSLUCENT, ) .apply { title = TAG @@ -188,10 +174,6 @@ constructor( val isHiding: Boolean get() = getTouchOverlay() == null - /** The animation controller if the overlay [isShowing]. */ - val animationViewController: UdfpsAnimationViewController<*>? - get() = overlayViewLegacy?.animationViewController - private var touchExplorationEnabled = false private fun shouldRemoveEnrollmentUi(): Boolean { @@ -199,7 +181,7 @@ constructor( return Settings.Global.getInt( context.contentResolver, SETTING_REMOVE_ENROLLMENT_UI, - 0 /* def */ + 0, /* def */ ) != 0 } return false @@ -212,63 +194,43 @@ constructor( overlayParams = params sensorBounds = Rect(params.sensorBounds) try { - if (DeviceEntryUdfpsRefactor.isEnabled) { - overlayTouchView = - (inflater.inflate(R.layout.udfps_touch_overlay, null, false) - as UdfpsTouchOverlay) - .apply { - // This view overlaps the sensor area - // prevent it from being selectable during a11y - if (requestReason.isImportantForAccessibility()) { - importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO - } - - addViewNowOrLater(this, null) - when (requestReason) { - REASON_AUTH_KEYGUARD -> - UdfpsTouchOverlayBinder.bind( - view = this, - viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(), - udfpsOverlayInteractor = udfpsOverlayInteractor, - ) - else -> - UdfpsTouchOverlayBinder.bind( - view = this, - viewModel = defaultUdfpsTouchOverlayViewModel.get(), - udfpsOverlayInteractor = udfpsOverlayInteractor, - ) - } - } - } else { - overlayViewLegacy = - (inflater.inflate(R.layout.udfps_view, null, false) as UdfpsView).apply { - overlayParams = params - setUdfpsDisplayModeProvider(udfpsDisplayModeProvider) - val animation = inflateUdfpsAnimation(this, controller) - if (animation != null) { - animation.init() - animationViewController = animation - } + overlayTouchView = + (inflater.inflate(R.layout.udfps_touch_overlay, null, false) + as UdfpsTouchOverlay) + .apply { // This view overlaps the sensor area // prevent it from being selectable during a11y if (requestReason.isImportantForAccessibility()) { importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO } - addViewNowOrLater(this, animation) - sensorRect = sensorBounds + addViewNowOrLater(this, null) + when (requestReason) { + REASON_AUTH_KEYGUARD -> + UdfpsTouchOverlayBinder.bind( + view = this, + viewModel = deviceEntryUdfpsTouchOverlayViewModel.get(), + udfpsOverlayInteractor = udfpsOverlayInteractor, + ) + else -> + UdfpsTouchOverlayBinder.bind( + view = this, + viewModel = defaultUdfpsTouchOverlayViewModel.get(), + udfpsOverlayInteractor = udfpsOverlayInteractor, + ) + } } - } + getTouchOverlay()?.apply { touchExplorationEnabled = accessibilityManager.isTouchExplorationEnabled overlayTouchListener = TouchExplorationStateChangeListener { if (accessibilityManager.isTouchExplorationEnabled) { - setOnHoverListener { v, event -> onTouch(v, event, true) } + setOnHoverListener { v, event -> onTouch(v, event) } setOnTouchListener(null) touchExplorationEnabled = true } else { setOnHoverListener(null) - setOnTouchListener { v, event -> onTouch(v, event, true) } + setOnTouchListener { v, event -> onTouch(v, event) } touchExplorationEnabled = false } } @@ -312,7 +274,6 @@ constructor( } fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) { - DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() overlayParams = updatedOverlayParams sensorBounds = updatedOverlayParams.sensorBounds getTouchOverlay()?.let { @@ -326,108 +287,11 @@ constructor( } } - fun inflateUdfpsAnimation( - view: UdfpsView, - controller: UdfpsController - ): UdfpsAnimationViewController<*>? { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - - val isEnrollment = - when (requestReason) { - REASON_ENROLL_FIND_SENSOR, - REASON_ENROLL_ENROLLING -> true - else -> false - } - - val filteredRequestReason = - if (isEnrollment && shouldRemoveEnrollmentUi()) { - REASON_AUTH_OTHER - } else { - requestReason - } - - return when (filteredRequestReason) { - REASON_ENROLL_FIND_SENSOR, - REASON_ENROLL_ENROLLING -> { - // Enroll udfps UI is handled by settings, so use empty view here - UdfpsFpmEmptyViewController( - view.addUdfpsView(R.layout.udfps_fpm_empty_view) { - updateAccessibilityViewLocation(sensorBounds) - }, - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_KEYGUARD -> { - UdfpsKeyguardViewControllerLegacy( - view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) { - updateSensorLocation(sensorBounds) - }, - statusBarStateController, - statusBarKeyguardViewManager, - keyguardUpdateMonitor, - dumpManager, - transitionController, - configurationController, - keyguardStateController, - unlockedScreenOffAnimationController, - dialogManager, - controller, - activityTransitionAnimator, - primaryBouncerInteractor, - alternateBouncerInteractor, - udfpsKeyguardAccessibilityDelegate, - selectedUserInteractor, - transitionInteractor, - shadeInteractor, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_BP -> { - // note: empty controller, currently shows no visual affordance - UdfpsBpViewController( - view.addUdfpsView(R.layout.udfps_bp_view), - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - REASON_AUTH_OTHER, - REASON_AUTH_SETTINGS -> { - UdfpsFpmEmptyViewController( - view.addUdfpsView(R.layout.udfps_fpm_empty_view), - statusBarStateController, - shadeInteractor, - dialogManager, - dumpManager, - udfpsOverlayInteractor, - ) - } - else -> { - Log.e(TAG, "Animation for reason $requestReason not supported yet") - null - } - } - } - /** Hide the overlay or return false and do nothing if it is already hidden. */ fun hide(): Boolean { val wasShowing = isShowing - overlayViewLegacy?.apply { - if (isDisplayConfigured) { - unconfigureDisplay() - } - animationViewController = null - } - if (DeviceEntryUdfpsRefactor.isEnabled) { - udfpsDisplayModeProvider.disable(null) - } + udfpsDisplayModeProvider.disable(null) getTouchOverlay()?.apply { if (this.parent != null) { windowManager.removeView(this) @@ -440,7 +304,6 @@ constructor( } } - overlayViewLegacy = null overlayTouchView = null overlayTouchListener = null listenForCurrentKeyguardState?.cancel() @@ -490,7 +353,7 @@ constructor( Surface.rotationToString(rot) + " animation=$animation" + " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" + - " isOccluded=${keyguardStateController.isOccluded}" + " isOccluded=${keyguardStateController.isOccluded}", ) } else { Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot)) @@ -498,14 +361,14 @@ constructor( rotatedBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, - rot + rot, ) RotationUtils.rotateBounds( sensorBounds, overlayParams.naturalDisplayWidth, overlayParams.naturalDisplayHeight, - rot + rot, ) } } @@ -519,14 +382,7 @@ constructor( } private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean { - val keyguardNotShowing = - if (DeviceEntryUdfpsRefactor.isEnabled) { - !keyguardStateController.isShowing - } else { - animation !is UdfpsKeyguardViewControllerLegacy - } - - if (keyguardNotShowing) { + if (!keyguardStateController.isShowing) { // always rotate view if we're not on the keyguard return true } @@ -534,16 +390,6 @@ constructor( // on the keyguard, make sure we don't rotate if we're going to sleep or not occluded return !(keyguardUpdateMonitor.isGoingToSleep || !keyguardStateController.isOccluded) } - - private inline fun <reified T : View> UdfpsView.addUdfpsView( - @LayoutRes id: Int, - init: T.() -> Unit = {} - ): T { - val subView = inflater.inflate(id, null) as T - addView(subView) - subView.init() - return subView - } } @RequestReason diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt deleted file mode 100644 index 0838855f4756..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import android.content.Context -import android.graphics.Rect -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import com.android.systemui.res.R - -/** - * View corresponding with udfps_fpm_empty_view.xml - * - * Currently doesn't draw anything. - */ -class UdfpsFpmEmptyView( - context: Context, - attrs: AttributeSet? -) : UdfpsAnimationView(context, attrs) { - - // Drawable isn't ever added to the view, so we don't currently show anything - private val fingerprintDrawable: UdfpsFpDrawable = UdfpsFpDrawable(context) - - override fun getDrawable(): UdfpsDrawable = fingerprintDrawable - - fun updateAccessibilityViewLocation(sensorBounds: Rect) { - val fingerprintAccessibilityView: View = - requireViewById(R.id.udfps_enroll_accessibility_view) - val params: ViewGroup.LayoutParams = fingerprintAccessibilityView.layoutParams - params.width = sensorBounds.width() - params.height = sensorBounds.height() - fingerprintAccessibilityView.layoutParams = params - fingerprintAccessibilityView.requestLayout() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt deleted file mode 100644 index cfbbc26800d2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.biometrics - -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.phone.SystemUIDialogManager - -/** - * Class that coordinates non-HBM animations for non keyguard, or biometric prompt states. - * - * Currently doesn't draw anything. - */ -class UdfpsFpmEmptyViewController( - view: UdfpsFpmEmptyView, - statusBarStateController: StatusBarStateController, - shadeInteractor: ShadeInteractor, - systemUIDialogManager: SystemUIDialogManager, - dumpManager: DumpManager, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : UdfpsAnimationViewController<UdfpsFpmEmptyView>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, -) { - override val tag = "UdfpsFpmOtherViewController" -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt deleted file mode 100644 index c3d9240c40a1..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics - -import android.content.res.Configuration -import android.util.MathUtils -import android.view.View -import androidx.annotation.VisibleForTesting -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.app.animation.Interpolators -import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress -import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.animation.ActivityTransitionAnimator -import com.android.systemui.biometrics.UdfpsKeyguardViewLegacy.ANIMATE_APPEAR_ON_SCREEN_OFF -import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.dump.DumpManager -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.Edge -import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER -import com.android.systemui.keyguard.shared.model.KeyguardState.AOD -import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING -import com.android.systemui.keyguard.shared.model.KeyguardState.GONE -import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED -import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.res.R -import com.android.systemui.scene.shared.model.Scenes -import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.OccludingAppBiometricUI -import com.android.systemui.statusbar.phone.SystemUIDialogManager -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.user.domain.interactor.SelectedUserInteractor -import java.io.PrintWriter -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch - -/** Class that coordinates non-HBM animations during keyguard authentication. */ -@ExperimentalCoroutinesApi -open class UdfpsKeyguardViewControllerLegacy( - private val view: UdfpsKeyguardViewLegacy, - statusBarStateController: StatusBarStateController, - private val keyguardViewManager: StatusBarKeyguardViewManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - dumpManager: DumpManager, - private val lockScreenShadeTransitionController: LockscreenShadeTransitionController, - private val configurationController: ConfigurationController, - private val keyguardStateController: KeyguardStateController, - private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, - systemUIDialogManager: SystemUIDialogManager, - private val udfpsController: UdfpsController, - private val activityTransitionAnimator: ActivityTransitionAnimator, - private val primaryBouncerInteractor: PrimaryBouncerInteractor, - private val alternateBouncerInteractor: AlternateBouncerInteractor, - private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, - private val selectedUserInteractor: SelectedUserInteractor, - private val transitionInteractor: KeyguardTransitionInteractor, - shadeInteractor: ShadeInteractor, - udfpsOverlayInteractor: UdfpsOverlayInteractor, -) : - UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>( - view, - statusBarStateController, - shadeInteractor, - systemUIDialogManager, - dumpManager, - udfpsOverlayInteractor, - ) { - private val uniqueIdentifier = this.toString() - private var showingUdfpsBouncer = false - private var udfpsRequested = false - private var qsExpansion = 0f - private var faceDetectRunning = false - private var statusBarState = 0 - private var transitionToFullShadeProgress = 0f - private var lastDozeAmount = 0f - private var panelExpansionFraction = 0f - private var launchTransitionFadingAway = false - private var isLaunchingActivity = false - private var activityLaunchProgress = 0f - private var inputBouncerExpansion = 0f - - private val stateListener: StatusBarStateController.StateListener = - object : StatusBarStateController.StateListener { - override fun onStateChanged(statusBarState: Int) { - this@UdfpsKeyguardViewControllerLegacy.statusBarState = statusBarState - updateAlpha() - updatePauseAuth() - } - } - - private val configurationListener: ConfigurationController.ConfigurationListener = - object : ConfigurationController.ConfigurationListener { - override fun onUiModeChanged() { - view.updateColor() - } - - override fun onThemeChanged() { - view.updateColor() - } - - override fun onConfigChanged(newConfig: Configuration) { - updateScaleFactor() - view.updatePadding() - view.updateColor() - } - } - - private val keyguardStateControllerCallback: KeyguardStateController.Callback = - object : KeyguardStateController.Callback { - override fun onUnlockedChanged() { - updatePauseAuth() - } - - override fun onLaunchTransitionFadingAwayChanged() { - launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway - updatePauseAuth() - } - } - - private val mActivityTransitionAnimatorListener: ActivityTransitionAnimator.Listener = - object : ActivityTransitionAnimator.Listener { - override fun onTransitionAnimationStart() { - isLaunchingActivity = true - activityLaunchProgress = 0f - updateAlpha() - } - - override fun onTransitionAnimationEnd() { - isLaunchingActivity = false - updateAlpha() - } - - override fun onTransitionAnimationProgress(linearProgress: Float) { - activityLaunchProgress = linearProgress - updateAlpha() - } - } - - private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback = - object : KeyguardViewManagerCallback { - override fun onQSExpansionChanged(qsExpansion: Float) { - this@UdfpsKeyguardViewControllerLegacy.qsExpansion = qsExpansion - updateAlpha() - updatePauseAuth() - } - } - - private val occludingAppBiometricUI: OccludingAppBiometricUI = - object : OccludingAppBiometricUI { - override fun requestUdfps(request: Boolean, color: Int) { - udfpsRequested = request - view.requestUdfps(request, color) - updateAlpha() - updatePauseAuth() - } - - override fun dump(pw: PrintWriter) { - pw.println(tag) - } - } - - override val tag: String - get() = TAG - - override fun onInit() { - super.onInit() - keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) - } - - init { - com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor.assertInLegacyMode() - view.repeatWhenAttached { - // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion - // can make the view not visible; and we still want to listen for events - // that may make the view visible again. - repeatOnLifecycle(Lifecycle.State.CREATED) { - listenForBouncerExpansion(this) - listenForAlternateBouncerVisibility(this) - listenForOccludedToAodTransition(this) - listenForGoneToAodTransition(this) - listenForLockscreenAodTransitions(this) - listenForAodToOccludedTransitions(this) - listenForAlternateBouncerToAodTransitions(this) - listenForDreamingToAodTransitions(this) - listenForPrimaryBouncerToAodTransitions(this) - } - } - } - - @VisibleForTesting - suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor - .transition( - edge = Edge.create(Scenes.Bouncer, AOD), - edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD) - ) - .collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect { - transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep -> - view.onDozeAmountChanged( - 1f - transitionStep.value, - 1f - transitionStep.value, - UdfpsKeyguardViewLegacy.ANIMATION_NONE, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor - .transition( - edge = Edge.create(Scenes.Gone, AOD), - edgeWithoutSceneContainer = Edge.create(GONE, AOD) - ) - .collect { transitionStep -> - view.onDozeAmountChanged( - transitionStep.value, - transitionStep.value, - ANIMATE_APPEAR_ON_SCREEN_OFF, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForLockscreenAodTransitions(scope: CoroutineScope): Job { - return scope.launch { - transitionInteractor.transitionValue(AOD).collect { - view.onDozeAmountChanged( - it, - it, - UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, - ) - } - } - } - - @VisibleForTesting - suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job { - return scope.launch { - primaryBouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float -> - inputBouncerExpansion = bouncerExpansion - - panelExpansionFraction = - if (keyguardViewManager.isPrimaryBouncerInTransit) { - aboutToShowBouncerProgress(1f - bouncerExpansion) - } else { - 1f - bouncerExpansion - } - updateAlpha() - updatePauseAuth() - } - } - } - - @VisibleForTesting - suspend fun listenForAlternateBouncerVisibility(scope: CoroutineScope): Job { - return scope.launch { - alternateBouncerInteractor.isVisible.collect { isVisible: Boolean -> - showUdfpsBouncer(isVisible) - } - } - } - - public override fun onViewAttached() { - super.onViewAttached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, uniqueIdentifier) - val dozeAmount = statusBarStateController.dozeAmount - lastDozeAmount = dozeAmount - stateListener.onDozeAmountChanged(dozeAmount, dozeAmount) - statusBarStateController.addCallback(stateListener) - udfpsRequested = false - launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway - keyguardStateController.addCallback(keyguardStateControllerCallback) - statusBarState = statusBarStateController.state - qsExpansion = keyguardViewManager.qsExpansion - keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback) - configurationController.addCallback(configurationListener) - updateScaleFactor() - view.updatePadding() - updateAlpha() - updatePauseAuth() - keyguardViewManager.setOccludingAppBiometricUI(occludingAppBiometricUI) - lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this - activityTransitionAnimator.addListener(mActivityTransitionAnimatorListener) - view.startIconAsyncInflate { - val animationViewInternal: View = - view.requireViewById(R.id.udfps_animation_view_internal) - animationViewInternal.accessibilityDelegate = udfpsKeyguardAccessibilityDelegate - } - } - - public override fun onViewDetached() { - super.onViewDetached() - alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier) - faceDetectRunning = false - keyguardStateController.removeCallback(keyguardStateControllerCallback) - statusBarStateController.removeCallback(stateListener) - keyguardViewManager.removeOccludingAppBiometricUI(occludingAppBiometricUI) - configurationController.removeCallback(configurationListener) - if (lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy === this) { - lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = null - } - activityTransitionAnimator.removeListener(mActivityTransitionAnimatorListener) - keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback) - } - - override fun dump(pw: PrintWriter, args: Array<String>) { - super.dump(pw, args) - pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer") - pw.println( - "altBouncerInteractor#isAlternateBouncerVisible=" + - "${alternateBouncerInteractor.isVisibleState()}" - ) - pw.println( - "altBouncerInteractor#canShowAlternateBouncerForFingerprint=" + - "${alternateBouncerInteractor.canShowAlternateBouncerForFingerprint()}" - ) - pw.println("faceDetectRunning=$faceDetectRunning") - pw.println("statusBarState=" + StatusBarState.toString(statusBarState)) - pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress") - pw.println("qsExpansion=$qsExpansion") - pw.println("panelExpansionFraction=$panelExpansionFraction") - pw.println("unpausedAlpha=" + view.unpausedAlpha) - pw.println("udfpsRequestedByApp=$udfpsRequested") - pw.println("launchTransitionFadingAway=$launchTransitionFadingAway") - pw.println("lastDozeAmount=$lastDozeAmount") - pw.println("inputBouncerExpansion=$inputBouncerExpansion") - view.dump(pw) - } - - /** - * Overrides non-bouncer show logic in shouldPauseAuth to still show icon. - * - * @return whether the udfpsBouncer has been newly shown or hidden - */ - private fun showUdfpsBouncer(show: Boolean): Boolean { - if (showingUdfpsBouncer == show) { - return false - } - val udfpsAffordanceWasNotShowing = shouldPauseAuth() - showingUdfpsBouncer = show - if (showingUdfpsBouncer) { - if (udfpsAffordanceWasNotShowing) { - view.animateInUdfpsBouncer(null) - } - view.announceForAccessibility( - view.context.getString(R.string.accessibility_fingerprint_bouncer) - ) - } - updateAlpha() - updatePauseAuth() - return true - } - - /** - * Returns true if the fingerprint manager is running but we want to temporarily pause - * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so - * this can be overridden with the showBouncer method. - */ - override fun shouldPauseAuth(): Boolean { - if (showingUdfpsBouncer) { - return false - } - if ( - udfpsRequested && - !notificationShadeVisible && - !isInputBouncerFullyVisible() && - keyguardStateController.isShowing - ) { - return false - } - if (launchTransitionFadingAway) { - return true - } - - // Only pause auth if we're not on the keyguard AND we're not transitioning to doze. - // For the UnlockedScreenOffAnimation, the statusBarState is - // delayed. However, we still animate in the UDFPS affordance with the - // unlockedScreenOffDozeAnimator. - if ( - statusBarState != StatusBarState.KEYGUARD && - !unlockedScreenOffAnimationController.isAnimationPlaying() - ) { - return true - } - if (isBouncerExpansionGreaterThan(.5f)) { - return true - } - if ( - keyguardUpdateMonitor.getUserUnlockedWithBiometric( - selectedUserInteractor.getSelectedUserId() - ) - ) { - // If the device was unlocked by a biometric, immediately hide the UDFPS icon to avoid - // overlap with the LockIconView. Shortly afterwards, UDFPS will stop running. - return true - } - return view.unpausedAlpha < 255 * .1 - } - - fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean { - return inputBouncerExpansion >= bouncerExpansionThreshold - } - - fun isInputBouncerFullyVisible(): Boolean { - return inputBouncerExpansion == 1f - } - - override fun listenForTouchesOutsideView(): Boolean { - return true - } - - /** - * Set the progress we're currently transitioning to the full shade. 0.0f means we're not - * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down - * to expand the notification shade from the empty space in the middle of the lock screen. - */ - fun setTransitionToFullShadeProgress(progress: Float) { - transitionToFullShadeProgress = progress - updateAlpha() - } - - /** - * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is - * based on the doze amount. - */ - override fun updateAlpha() { - // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested, - // then the keyguard is occluded by some application - so instead use the input bouncer - // hidden amount to determine the fade. - val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction - var alpha: Int = - if (showingUdfpsBouncer) 255 - else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt() - if (!showingUdfpsBouncer) { - // swipe from top of the lockscreen to expand full QS: - alpha = - (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion))) - .toInt() - - // swipe from the middle (empty space) of lockscreen to expand the notification shade: - alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt() - - // Fade out the icon if we are animating an activity launch over the lockscreen and the - // activity didn't request the UDFPS. - if (isLaunchingActivity && !udfpsRequested) { - val udfpsActivityLaunchAlphaMultiplier = - 1f - - (activityLaunchProgress * - (ActivityTransitionAnimator.TIMINGS.totalDuration / 83)) - .coerceIn(0f, 1f) - alpha = (alpha * udfpsActivityLaunchAlphaMultiplier).toInt() - } - - // Fade out alpha when a dialog is shown - // Fade in alpha when a dialog is hidden - alpha = (alpha * view.dialogSuggestedAlpha).toInt() - } - view.unpausedAlpha = alpha - } - - private fun getInputBouncerHiddenAmt(): Float { - return 1f - inputBouncerExpansion - } - - /** Update the scale factor based on the device's resolution. */ - private fun updateScaleFactor() { - udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) } - } - - companion object { - const val TAG = "UdfpsKeyguardViewController" - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java deleted file mode 100644 index 6d4eea852d26..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.biometrics; - -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.AttributeSet; -import android.util.MathUtils; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; - -import androidx.annotation.IntDef; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.asynclayoutinflater.view.AsyncLayoutInflater; - -import com.android.app.animation.Interpolators; -import com.android.settingslib.Utils; -import com.android.systemui.res.R; - -import com.airbnb.lottie.LottieAnimationView; -import com.airbnb.lottie.LottieProperty; -import com.airbnb.lottie.model.KeyPath; - -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * View corresponding with udfps_keyguard_view_legacy.xml - */ -public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { - private UdfpsDrawable mFingerprintDrawable; // placeholder - private LottieAnimationView mAodFp; - private LottieAnimationView mLockScreenFp; - - // used when highlighting fp icon: - private int mTextColorPrimary; - private ImageView mBgProtection; - boolean mUdfpsRequested; - - private AnimatorSet mBackgroundInAnimator = new AnimatorSet(); - private int mAlpha; // 0-255 - private float mScaleFactor = 1; - private Rect mSensorBounds = new Rect(); - - // AOD anti-burn-in offsets - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; - private float mInterpolatedDarkAmount; - private int mAnimationType = ANIMATION_NONE; - private boolean mFullyInflated; - private Runnable mOnFinishInflateRunnable; - - public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mFingerprintDrawable = new UdfpsFpDrawable(context); - - mMaxBurnInOffsetX = context.getResources() - .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); - mMaxBurnInOffsetY = context.getResources() - .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - } - - /** - * Inflate internal udfps view on a background thread and call the onFinishRunnable - * when inflation is finished. - */ - public void startIconAsyncInflate(Runnable onFinishInflate) { - mOnFinishInflateRunnable = onFinishInflate; - // inflate Lottie views on a background thread in case it takes a while to inflate - AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext); - inflater.inflate(R.layout.udfps_keyguard_view_internal, this, - mLayoutInflaterFinishListener); - } - - @Override - public UdfpsDrawable getDrawable() { - return mFingerprintDrawable; - } - - @Override - void onSensorRectUpdated(RectF bounds) { - super.onSensorRectUpdated(bounds); - bounds.round(this.mSensorBounds); - postInvalidate(); - } - - @Override - void onDisplayConfiguring() { - } - - @Override - void onDisplayUnconfigured() { - } - - @Override - public boolean dozeTimeTick() { - updateBurnInOffsets(); - return true; - } - - private void updateBurnInOffsets() { - if (!mFullyInflated) { - return; - } - - // if we're animating from screen off, we can immediately place the icon in the - // AoD-burn in location, else we need to translate the icon from LS => AoD. - final float darkAmountForAnimation = mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF - ? 1f : mInterpolatedDarkAmount; - final float burnInOffsetX = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) - - mMaxBurnInOffsetX, darkAmountForAnimation); - final float burnInOffsetY = MathUtils.lerp(0f, - getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - - mMaxBurnInOffsetY, darkAmountForAnimation); - final float burnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), - darkAmountForAnimation); - - if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) { - mLockScreenFp.setTranslationX(burnInOffsetX); - mLockScreenFp.setTranslationY(burnInOffsetY); - mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); - mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount); - } else if (darkAmountForAnimation == 0f) { - // we're on the lockscreen and should use mAlpha (changes based on shade expansion) - mLockScreenFp.setTranslationX(0); - mLockScreenFp.setTranslationY(0); - mBgProtection.setAlpha(mAlpha / 255f); - mLockScreenFp.setAlpha(mAlpha / 255f); - } else { - mBgProtection.setAlpha(0f); - mLockScreenFp.setAlpha(0f); - } - mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount); - - mAodFp.setTranslationX(burnInOffsetX); - mAodFp.setTranslationY(burnInOffsetY); - mAodFp.setProgress(burnInProgress); - mAodFp.setAlpha(mInterpolatedDarkAmount); - - // done animating - final boolean doneAnimatingBetweenAodAndLS = - mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN - && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f); - final boolean doneAnimatingUnlockedScreenOff = - mAnimationType == ANIMATE_APPEAR_ON_SCREEN_OFF - && (mInterpolatedDarkAmount == 1f); - if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) { - mAnimationType = ANIMATION_NONE; - } - } - - void requestUdfps(boolean request, int color) { - mUdfpsRequested = request; - } - - void updateColor() { - if (!mFullyInflated) { - return; - } - - mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, - com.android.internal.R.attr.materialColorOnSurface); - final int backgroundColor = Utils.getColorAttrDefaultColor(getContext(), - com.android.internal.R.attr.materialColorSurfaceContainerHigh); - mBgProtection.setImageTintList(ColorStateList.valueOf(backgroundColor)); - mLockScreenFp.invalidate(); // updated with a valueCallback - } - - void setScaleFactor(float scale) { - mScaleFactor = scale; - } - - void updatePadding() { - if (mLockScreenFp == null || mAodFp == null) { - return; - } - - final int defaultPaddingPx = - getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); - final int padding = (int) (defaultPaddingPx * mScaleFactor); - mLockScreenFp.setPadding(padding, padding, padding, padding); - mAodFp.setPadding(padding, padding, padding, padding); - } - - /** - * @param alpha between 0 and 255 - */ - void setUnpausedAlpha(int alpha) { - mAlpha = alpha; - updateAlpha(); - } - - /** - * @return alpha between 0 and 255 - */ - int getUnpausedAlpha() { - return mAlpha; - } - - @Override - protected int updateAlpha() { - int alpha = super.updateAlpha(); - updateBurnInOffsets(); - return alpha; - } - - @Override - int calculateAlpha() { - if (mPauseAuth) { - return 0; - } - return mAlpha; - } - - static final int ANIMATION_NONE = 0; - static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1; - static final int ANIMATE_APPEAR_ON_SCREEN_OFF = 2; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATE_APPEAR_ON_SCREEN_OFF}) - private @interface AnimationType {} - - void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) { - mAnimationType = animationType; - mInterpolatedDarkAmount = eased; - updateAlpha(); - } - - void updateSensorLocation(@NonNull Rect sensorBounds) { - mSensorBounds.set(sensorBounds); - } - - /** - * Animates in the bg protection circle behind the fp icon to highlight the icon. - */ - void animateInUdfpsBouncer(Runnable onEndAnimation) { - if (mBackgroundInAnimator.isRunning() || !mFullyInflated) { - // already animating in or not yet inflated - return; - } - - // fade in and scale up - mBackgroundInAnimator = new AnimatorSet(); - mBackgroundInAnimator.playTogether( - ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), - ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), - ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f)); - mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mBackgroundInAnimator.setDuration(500); - mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (onEndAnimation != null) { - onEndAnimation.run(); - } - } - }); - mBackgroundInAnimator.start(); - } - - /** - * Print debugging information for this class. - */ - public void dump(PrintWriter pw) { - pw.println("UdfpsKeyguardView (" + this + ")"); - pw.println(" mPauseAuth=" + mPauseAuth); - pw.println(" mUnpausedAlpha=" + getUnpausedAlpha()); - pw.println(" mUdfpsRequested=" + mUdfpsRequested); - pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount); - pw.println(" mAnimationType=" + mAnimationType); - } - - private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener = - new AsyncLayoutInflater.OnInflateFinishedListener() { - @Override - public void onInflateFinished(View view, int resid, ViewGroup parent) { - mFullyInflated = true; - mAodFp = view.findViewById(R.id.udfps_aod_fp); - mLockScreenFp = view.findViewById(R.id.udfps_lockscreen_fp); - mBgProtection = view.findViewById(R.id.udfps_keyguard_fp_bg); - - updatePadding(); - updateColor(); - updateAlpha(); - - final LayoutParams lp = (LayoutParams) view.getLayoutParams(); - lp.width = mSensorBounds.width(); - lp.height = mSensorBounds.height(); - RectF relativeToView = getBoundsRelativeToView(new RectF(mSensorBounds)); - lp.setMarginsRelative((int) relativeToView.left, (int) relativeToView.top, - (int) relativeToView.right, (int) relativeToView.bottom); - parent.addView(view, lp); - - // requires call to invalidate to update the color - mLockScreenFp.addValueCallback(new KeyPath("**"), LottieProperty.COLOR_FILTER, - frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, - PorterDuff.Mode.SRC_ATOP)); - mOnFinishInflateRunnable.run(); - } - }; -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt index 7503a8b4362d..a105d663424c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt @@ -23,7 +23,6 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.biometrics.ui.view.UdfpsTouchOverlay import com.android.systemui.biometrics.ui.viewmodel.UdfpsTouchOverlayViewModel -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.lifecycle.repeatWhenAttached import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch @@ -42,14 +41,13 @@ object UdfpsTouchOverlayBinder { viewModel: UdfpsTouchOverlayViewModel, udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) return view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { launch { viewModel.shouldHandleTouches.collect { shouldHandleTouches -> Log.d( "UdfpsTouchOverlayBinder", - "[$view]: update shouldHandleTouches=$shouldHandleTouches" + "[$view]: update shouldHandleTouches=$shouldHandleTouches", ) view.isInvisible = !shouldHandleTouches udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches) @@ -58,7 +56,7 @@ object UdfpsTouchOverlayBinder { .invokeOnCompletion { Log.d( "UdfpsTouchOverlayBinder", - "[$view-detached]: update shouldHandleTouches=false" + "[$view-detached]: update shouldHandleTouches=false", ) udfpsOverlayInteractor.setHandleTouches(false) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt index f1c3f949ffba..22b2888a51a9 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt @@ -24,7 +24,6 @@ import com.android.systemui.bouncer.shared.model.BouncerDismissActionModel import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.log.dagger.BouncerTableLog import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable @@ -88,7 +87,6 @@ interface KeyguardBouncerRepository { val showMessage: StateFlow<BouncerShowMessageModel?> val resourceUpdateRequests: StateFlow<Boolean> val alternateBouncerVisible: StateFlow<Boolean> - val alternateBouncerUIAvailable: StateFlow<Boolean> /** Last shown security mode of the primary bouncer (ie: pin/pattern/password/SIM) */ val lastShownSecurityMode: StateFlow<KeyguardSecurityModel.SecurityMode> @@ -126,8 +124,6 @@ interface KeyguardBouncerRepository { fun setAlternateVisible(isVisible: Boolean) - fun setAlternateBouncerUIAvailable(isAvailable: Boolean) - fun setLastShownSecurityMode(securityMode: KeyguardSecurityModel.SecurityMode) } @@ -199,9 +195,6 @@ constructor( private val _alternateBouncerVisible = MutableStateFlow(false) override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow() override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE - private val _alternateBouncerUIAvailable = MutableStateFlow(false) - override val alternateBouncerUIAvailable: StateFlow<Boolean> = - _alternateBouncerUIAvailable.asStateFlow() init { setUpLogging() @@ -220,11 +213,6 @@ constructor( _alternateBouncerVisible.value = isVisible } - override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - _alternateBouncerUIAvailable.value = isAvailable - } - override fun setPrimaryShow(isShowing: Boolean) { _primaryBouncerShow.value = isShowing } @@ -320,9 +308,6 @@ constructor( resourceUpdateRequests .logDiffsForTable(buffer, "", "ResourceUpdateRequests", false) .launchIn(applicationScope) - alternateBouncerUIAvailable - .logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false) - .launchIn(applicationScope) alternateBouncerVisible .logDiffsForTable(buffer, "", "AlternateBouncerVisible", false) .launchIn(applicationScope) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 0949ea4d7797..9c2a10a444e2 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -17,22 +17,17 @@ package com.android.systemui.bouncer.domain.interactor import android.util.Log -import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.plugins.statusbar.StatusBarStateController 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.policy.KeyguardStateController import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.time.SystemClock import dagger.Lazy @@ -55,13 +50,9 @@ import kotlinx.coroutines.flow.stateIn class AlternateBouncerInteractor @Inject constructor( - private val statusBarStateController: StatusBarStateController, - private val keyguardStateController: KeyguardStateController, private val bouncerRepository: KeyguardBouncerRepository, fingerprintPropertyRepository: FingerprintPropertyRepository, - private val biometricSettingsRepository: BiometricSettingsRepository, private val systemClock: SystemClock, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val deviceEntryBiometricsAllowedInteractor: Lazy<DeviceEntryBiometricsAllowedInteractor>, private val keyguardInteractor: Lazy<KeyguardInteractor>, @@ -73,17 +64,10 @@ constructor( val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet() val alternateBouncerSupported: StateFlow<Boolean> = - if (DeviceEntryUdfpsRefactor.isEnabled) { - fingerprintPropertyRepository.sensorType - .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } - .stateIn( - scope = scope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - } else { - bouncerRepository.alternateBouncerUIAvailable - } + fingerprintPropertyRepository.sensorType + .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } + .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false) + private val isDozingOrAod: Flow<Boolean> = anyOf( keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map { @@ -106,7 +90,7 @@ constructor( combine( keyguardTransitionInteractor.get().currentKeyguardState, sceneInteractor.get().currentScene, - ::Pair + ::Pair, ) .flatMapLatest { (currentKeyguardState, transitionState) -> if (currentKeyguardState == KeyguardState.GONE) { @@ -122,7 +106,7 @@ constructor( .isFingerprintAuthCurrentlyAllowed, keyguardInteractor.get().isKeyguardDismissible, bouncerRepository.primaryBouncerShow, - isDozingOrAod + isDozingOrAod, ) { fingerprintAllowed, keyguardDismissible, @@ -141,37 +125,17 @@ constructor( } .distinctUntilChanged() .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") } - .stateIn( - scope = scope, - started = WhileSubscribed(), - initialValue = false, - ) + .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false) /** * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before * calling this. */ fun forceShow() { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { - show() - return - } bouncerRepository.setAlternateVisible(true) } /** - * Sets the correct bouncer states to show the alternate bouncer if it can show. - * - * @return whether alternateBouncer is visible - * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand - */ - fun show(): Boolean { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint()) - return isVisibleState() - } - - /** * Sets the correct bouncer states to hide the bouncer. Should only be called through * StatusBarKeyguardViewManager until ScrimController is refactored to use * alternateBouncerInteractor. @@ -189,28 +153,8 @@ constructor( return bouncerRepository.alternateBouncerVisible.value } - fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) { - DeviceEntryUdfpsRefactor.assertInLegacyMode() - if (isAvailable) { - alternateBouncerUiAvailableFromSource.add(token) - } else { - alternateBouncerUiAvailableFromSource.remove(token) - } - bouncerRepository.setAlternateBouncerUIAvailable( - alternateBouncerUiAvailableFromSource.isNotEmpty() - ) - } - fun canShowAlternateBouncerForFingerprint(): Boolean { - if (DeviceEntryUdfpsRefactor.isEnabled) { - return canShowAlternateBouncer.value - } - return alternateBouncerSupported.value && - biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value && - !keyguardUpdateMonitor.isFingerprintLockedOut && - !keyguardStateController.isUnlocked && - !statusBarStateController.isDozing && - !bouncerRepository.primaryBouncerShow.value + return canShowAlternateBouncer.value } /** diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt index 4185aed3095c..148b9ea61e2c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.bouncer.ui.viewmodel import android.annotation.StringRes -import com.android.app.tracing.coroutines.flow.collectLatest import com.android.systemui.authentication.domain.interactor.AuthenticationResult import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor @@ -28,6 +27,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow sealed class AuthMethodBouncerViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt index 8270db1e89a8..470807985006 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt @@ -18,6 +18,8 @@ package com.android.systemui.brightness.ui.compose import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.material3.MaterialTheme @@ -33,12 +35,17 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.PlatformSlider +import com.android.systemui.Flags import com.android.systemui.brightness.shared.model.GammaBrightness import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel import com.android.systemui.brightness.ui.viewmodel.Drag import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.common.ui.compose.Icon +import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig +import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel +import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.utils.PolicyRestriction import kotlinx.coroutines.launch @@ -54,12 +61,30 @@ private fun BrightnessSlider( onStop: (Int) -> Unit, modifier: Modifier = Modifier, formatter: (Int) -> String = { "$it" }, + hapticsViewModelFactory: SliderHapticsViewModel.Factory, ) { var value by remember(gammaValue) { mutableIntStateOf(gammaValue) } val animatedValue by animateFloatAsState(targetValue = value.toFloat(), label = "BrightnessSliderAnimatedValue") val floatValueRange = valueRange.first.toFloat()..valueRange.last.toFloat() val isRestricted = remember(restriction) { restriction is PolicyRestriction.Restricted } + val interactionSource = remember { MutableInteractionSource() } + val hapticsViewModel: SliderHapticsViewModel? = + if (Flags.hapticsForComposeSliders()) { + rememberViewModel(traceName = "SliderHapticsViewModel") { + hapticsViewModelFactory.create( + interactionSource, + floatValueRange, + Orientation.Horizontal, + SliderHapticFeedbackConfig( + maxVelocityToScale = 1f /* slider progress(from 0 to 1) per sec */ + ), + SeekableSliderTrackerConfig(), + ) + } + } else { + null + } PlatformSlider( value = animatedValue, @@ -67,19 +92,19 @@ private fun BrightnessSlider( enabled = !isRestricted, onValueChange = { if (!isRestricted) { + hapticsViewModel?.onValueChange(it) value = it.toInt() onDrag(value) } }, onValueChangeFinished = { if (!isRestricted) { + hapticsViewModel?.onValueChangeEnded() onStop(value) } }, modifier = - modifier.clickable( - enabled = isRestricted, - ) { + modifier.clickable(enabled = isRestricted) { if (restriction is PolicyRestriction.Restricted) { onRestrictedClick(restriction) } @@ -98,14 +123,12 @@ private fun BrightnessSlider( maxLines = 1, ) }, + interactionSource = interactionSource, ) } @Composable -fun BrightnessSliderContainer( - viewModel: BrightnessSliderViewModel, - modifier: Modifier = Modifier, -) { +fun BrightnessSliderContainer(viewModel: BrightnessSliderViewModel, modifier: Modifier = Modifier) { val state by viewModel.currentBrightness.collectAsStateWithLifecycle() val gamma = state.value val coroutineScope = rememberCoroutineScope() @@ -125,5 +148,6 @@ fun BrightnessSliderContainer( onStop = { coroutineScope.launch { viewModel.onDrag(Drag.Stopped(GammaBrightness(it))) } }, modifier = modifier.fillMaxWidth(), formatter = viewModel::formatValue, + hapticsViewModelFactory = viewModel.hapticsViewModelFactory, ) } diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt index 16a1dcc0aef5..074ac5003c67 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt @@ -24,6 +24,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.res.R import com.android.systemui.utils.PolicyRestriction import javax.inject.Inject @@ -38,12 +39,13 @@ constructor( private val screenBrightnessInteractor: ScreenBrightnessInteractor, private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor, @Application private val applicationScope: CoroutineScope, + val hapticsViewModelFactory: SliderHapticsViewModel.Factory, ) { val currentBrightness = screenBrightnessInteractor.gammaBrightness.stateIn( applicationScope, SharingStarted.WhileSubscribed(), - GammaBrightness(0) + GammaBrightness(0), ) val maxBrightness = screenBrightnessInteractor.maxGammaBrightness @@ -85,6 +87,8 @@ constructor( /** Represents a drag event in a brightness slider. */ sealed interface Drag { val brightness: GammaBrightness + @JvmInline value class Dragging(override val brightness: GammaBrightness) : Drag + @JvmInline value class Stopped(override val brightness: GammaBrightness) : Drag } diff --git a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt index e3f1174d4a5f..a695163ed155 100644 --- a/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/common/usagestats/data/repository/UsageStatsRepository.kt @@ -19,7 +19,7 @@ package com.android.systemui.common.usagestats.data.repository import android.app.usage.UsageEvents import android.app.usage.UsageEventsQuery import android.app.usage.UsageStatsManager -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.common.usagestats.data.model.UsageStatsQuery import com.android.systemui.common.usagestats.shared.model.ActivityEventModel import com.android.systemui.common.usagestats.shared.model.ActivityEventModel.Lifecycle diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index 08a7c395e57f..8510915d55af 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -16,6 +16,7 @@ package com.android.systemui.communal +import android.os.UserHandle import android.provider.Settings import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey @@ -147,9 +148,10 @@ constructor( .emitOnStart() .onEach { screenTimeout = - systemSettings.getInt( + systemSettings.getIntForUser( Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_TIMEOUT, + UserHandle.USER_CURRENT, ) } .launchIn(bgScope) 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 3a04d026ee84..dc248059b3d4 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 @@ -23,7 +23,7 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.os.UserManager import android.provider.Settings -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt index 3826fb40ea3d..428b83ddbb3a 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt @@ -16,7 +16,7 @@ package com.android.systemui.communal.domain.interactor -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.TransitionKey diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt index ba96f4e56421..71bfe0c057e4 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/binder/CommunalAppWidgetHostViewBinder.kt @@ -24,8 +24,7 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.compose.ui.unit.IntSize import androidx.core.view.doOnLayout -import com.android.app.tracing.coroutines.flow.flowOn -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalWidgetResizing import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.communal.domain.model.CommunalContentModel @@ -41,6 +40,7 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn object CommunalAppWidgetHostViewBinder { private const val TAG = "CommunalAppWidgetHostViewBinder" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt index 87fcdd7b97ee..a519649a8f1c 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/ResizeableItemFrameViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.ui.viewmodel import androidx.compose.foundation.gestures.AnchoredDraggableState import androidx.compose.foundation.gestures.DraggableAnchors import androidx.compose.runtime.snapshotFlow -import com.android.app.tracing.coroutines.coroutineScope +import com.android.app.tracing.coroutines.coroutineScopeTraced as coroutineScope import com.android.systemui.lifecycle.ExclusiveActivatable import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.Flow diff --git a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt index 07a7c7cba2fd..d5d3a927181b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/util/WidgetViewFactory.kt @@ -19,7 +19,7 @@ package com.android.systemui.communal.util import android.content.Context import android.os.Bundle import android.util.SizeF -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.widgets.AppWidgetHostListenerDelegate import com.android.systemui.communal.widgets.CommunalAppWidgetHost diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt index 542b98896986..c894267a61dd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt @@ -21,7 +21,7 @@ import android.app.PendingIntent import android.content.Intent import android.view.View import android.widget.RemoteViews -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.Flags.communalWidgetTrampolineFix import com.android.systemui.animation.ActivityTransitionAnimator diff --git a/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.kt new file mode 100644 index 000000000000..5b1c9c8b8020 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/coroutines/Tracing.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.systemui.coroutines + +import com.android.app.tracing.coroutines.createCoroutineTracingContext +import kotlin.coroutines.CoroutineContext + +fun newTracingContext(name: String): CoroutineContext { + return createCoroutineTracingContext(name) { className -> + className.startsWith("com.android.systemui.util.kotlin.JavaAdapter") || + className.startsWith("com.android.systemui.lifecycle.RepeatWhenAttached") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.java new file mode 100644 index 000000000000..1950d6bac251 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Default.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 com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface Default { +} diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index b8c03c071572..c464a66ea0c3 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -17,25 +17,17 @@ package com.android.systemui.deviceentry import com.android.keyguard.EmptyLockIconViewController -import com.android.keyguard.LegacyLockIconViewController import com.android.keyguard.LockIconViewController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.Multibinds -@Module( - includes = - [ - DeviceEntryRepositoryModule::class, - FaceWakeUpTriggersConfigModule::class, - ], -) +@Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class]) abstract class DeviceEntryModule { /** * A set of DeviceEntryIconTransitions. Ensures that this can be injected even if it's empty. @@ -46,14 +38,9 @@ abstract class DeviceEntryModule { @Provides @SysUISingleton fun provideLockIconViewController( - legacyLockIconViewController: Lazy<LegacyLockIconViewController>, - emptyLockIconViewController: Lazy<EmptyLockIconViewController>, + emptyLockIconViewController: Lazy<EmptyLockIconViewController> ): LockIconViewController { - return if (DeviceEntryUdfpsRefactor.isEnabled) { - emptyLockIconViewController.get() - } else { - legacyLockIconViewController.get() - } + return emptyLockIconViewController.get() } } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt index 6a6913677a0c..034cb31dbc74 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt @@ -33,6 +33,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.DisplayEvent import com.android.systemui.util.Compile +import com.android.systemui.util.kotlin.pairwiseBy import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -41,11 +42,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -146,11 +148,6 @@ constructor( override val displayChangeEvent: Flow<Int> = allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId } - override val displayAdditionEvent: Flow<Display?> = - allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { - getDisplayFromDisplayManager(it.displayId) - } - override val displayRemovalEvent: Flow<Int> = allDisplayEvents.filterIsInstance<DisplayEvent.Removed>().map { it.displayId } @@ -212,6 +209,17 @@ constructor( */ override val displays: StateFlow<Set<Display>> = enabledDisplays + /** + * Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons: + * 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that + * calling [getDisplay] for the newly added display will be non-null. + * 2. Reuse the existing instance of [Display] without a new call to [DisplayManager]. + */ + override val displayAdditionEvent: Flow<Display?> = + displays + .pairwiseBy { previousDisplays, currentDisplays -> currentDisplays - previousDisplays } + .flatMapLatest { it.asFlow() } + val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet()) private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds") diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt index 30624757ebe3..e3fce0007f26 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt @@ -17,8 +17,8 @@ package com.android.systemui.display.data.repository import android.view.Display -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.systemui.CoreStartable +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.core.StatusBarConnectedDisplays @@ -69,7 +69,7 @@ constructor( backgroundApplicationScope } else { CoroutineScope( - backgroundDispatcher + createCoroutineTracingContext("DisplayScope$displayId") + backgroundDispatcher + newTracingContext("DisplayScope$displayId") ) } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt index 3992c3fb70b0..724f1c5323cf 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt @@ -22,6 +22,7 @@ import android.service.controls.ControlsProviderService import android.service.dreams.DreamService import android.window.TaskFragmentInfo import com.android.systemui.controls.settings.ControlsSettingsRepository +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dreams.DreamLogger import com.android.systemui.dreams.homecontrols.domain.interactor.HomeControlsComponentInteractor @@ -39,7 +40,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import com.android.app.tracing.coroutines.createCoroutineTracingContext class HomeControlsDreamService @Inject @@ -54,7 +54,8 @@ constructor( ) : DreamService() { private val serviceJob = SupervisorJob() - private val serviceScope = CoroutineScope(bgDispatcher + serviceJob + createCoroutineTracingContext("HomeControlsDreamService")) + private val serviceScope = + CoroutineScope(bgDispatcher + serviceJob + newTracingContext("HomeControlsDreamService")) private val logger = DreamLogger(logBuffer, TAG) private lateinit var taskFragmentComponent: TaskFragmentComponent private val wakeLock: WakeLock by lazy { diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt index 4caf95b707b1..7fa7da192ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt +++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt @@ -16,10 +16,10 @@ package com.android.systemui.education.dagger -import com.android.app.tracing.coroutines.createCoroutineTracingContext import com.android.systemui.CoreStartable import com.android.systemui.Flags import com.android.systemui.contextualeducation.GestureType +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.education.data.repository.UserContextualEducationRepository @@ -57,7 +57,9 @@ interface ContextualEducationModule { fun provideEduDataStoreScope( @Background bgDispatcher: CoroutineDispatcher ): CoroutineScope { - return CoroutineScope(bgDispatcher + SupervisorJob() + createCoroutineTracingContext("EduDataStoreScope")) + return CoroutineScope( + bgDispatcher + SupervisorJob() + newTracingContext("EduDataStoreScope") + ) } @EduClock diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index aa1873c7ad41..162047bb3b79 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -135,6 +135,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.util.EmergencyDialerConstants; @@ -248,7 +249,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final IStatusBarService mStatusBarService; protected final LightBarController mLightBarController; protected final NotificationShadeWindowController mNotificationShadeWindowController; - private final StatusBarWindowController mStatusBarWindowController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; private final RingerModeTracker mRingerModeTracker; @@ -364,7 +365,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene IStatusBarService statusBarService, LightBarController lightBarController, NotificationShadeWindowController notificationShadeWindowController, - StatusBarWindowController statusBarWindowController, + StatusBarWindowControllerStore statusBarWindowControllerStore, IWindowManager iWindowManager, @Background Executor backgroundExecutor, UiEventLogger uiEventLogger, @@ -400,7 +401,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mStatusBarService = statusBarService; mLightBarController = lightBarController; mNotificationShadeWindowController = notificationShadeWindowController; - mStatusBarWindowController = statusBarWindowController; + mStatusBarWindowControllerStore = statusBarWindowControllerStore; mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; mRingerModeTracker = ringerModeTracker; @@ -708,7 +709,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mLightBarController, mKeyguardStateController, mNotificationShadeWindowController, - mStatusBarWindowController, + mStatusBarWindowControllerStore.getDefaultDisplay(), this::onRefresh, mKeyguardShowing, mPowerAdapter, diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt index 58736c608af3..0c9faddda217 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/MSDLCoreStartable.kt @@ -28,7 +28,8 @@ class MSDLCoreStartable @Inject constructor(private val msdlPlayer: MSDLPlayer) override fun start() {} override fun dump(pw: PrintWriter, args: Array<out String>) { - pw.println("MSDLPlayer history of the last ${MSDLHistoryLogger.HISTORY_SIZE} events:") + pw.println(msdlPlayer) + pw.println("MSDL player history of the last ${MSDLHistoryLogger.HISTORY_SIZE} events:") msdlPlayer.getHistory().forEach { event -> pw.println("$event") } } } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt index d2dc8c1e8328..108d5b12f070 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/msdl/dagger/MSDLModule.kt @@ -17,10 +17,8 @@ package com.android.systemui.haptics.msdl.dagger import android.annotation.SuppressLint -import android.content.Context -import android.os.VibratorManager +import android.os.Vibrator import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.google.android.msdl.domain.MSDLPlayer import dagger.Module import dagger.Provides @@ -30,9 +28,5 @@ object MSDLModule { @SuppressLint("NonInjectedService") @Provides @SysUISingleton - fun provideMSDLPlayer(@Application context: Context): MSDLPlayer { - val vibratorManager = - context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager - return MSDLPlayer.createPlayer(vibratorManager.defaultVibrator) - } + fun provideMSDLPlayer(vibrator: Vibrator?): MSDLPlayer = MSDLPlayer.createPlayer(vibrator) } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt index 2007db3448e2..932e5af6244b 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekbarHapticPlugin.kt @@ -46,12 +46,24 @@ constructor( private val velocityTracker = VelocityTracker.obtain() + private val dragVelocityProvider = SliderDragVelocityProvider { + velocityTracker.computeCurrentVelocity( + UNITS_SECOND, + sliderHapticFeedbackConfig.maxVelocityToScale, + ) + if (velocityTracker.isAxisSupported(sliderHapticFeedbackConfig.velocityAxis)) { + velocityTracker.getAxisVelocity(sliderHapticFeedbackConfig.velocityAxis) + } else { + 0f + } + } + private val sliderEventProducer = SliderStateProducer() private val sliderHapticFeedbackProvider = SliderHapticFeedbackProvider( vibratorHelper, - velocityTracker, + dragVelocityProvider, sliderHapticFeedbackConfig, systemClock, ) @@ -188,5 +200,6 @@ constructor( companion object { const val KEY_UP_TIMEOUT = 60L + private const val UNITS_SECOND = 1000 } } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderDragVelocityProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderDragVelocityProvider.kt new file mode 100644 index 000000000000..682932636d54 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderDragVelocityProvider.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics.slider + +/** A provider of the velocity at which a slider is being dragged */ +fun interface SliderDragVelocityProvider { + + /** + * Get the velocity of the slider at the time this function is called. + * + * @return the velocity of the drag in pixels/sec + */ + fun getTrackedVelocity(): Float +} diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt index 89bfd96d2408..24dd04dbadd2 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackConfig.kt @@ -38,7 +38,7 @@ data class SliderHapticFeedbackConfig( /** Number of low ticks in a drag texture composition. This is not expected to change */ val numberOfLowTicks: Int = 5, /** Maximum velocity allowed for vibration scaling. This is not expected to change. */ - val maxVelocityToScale: Float = 2000f, /* In pixels/sec */ + val maxVelocityToScale: Float = 2000f, /* In units/sec. The default units are pixels */ /** Axis to use when computing velocity. Must be the same as the slider's axis of movement */ val velocityAxis: Int = MotionEvent.AXIS_X, /** Vibration scale at the upper bookend of the slider */ diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt index 6f28ab7f414c..06428b77f759 100644 --- a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderHapticFeedbackProvider.kt @@ -38,7 +38,7 @@ import kotlin.math.pow */ class SliderHapticFeedbackProvider( private val vibratorHelper: VibratorHelper, - private val velocityTracker: VelocityTracker, + private val velocityProvider: SliderDragVelocityProvider, private val config: SliderHapticFeedbackConfig = SliderHapticFeedbackConfig(), private val clock: com.android.systemui.util.time.SystemClock, ) : SliderStateListener { @@ -50,6 +50,7 @@ class SliderHapticFeedbackProvider( private var dragTextureLastTime = clock.elapsedRealtime() var dragTextureLastProgress = -1f private set + private val lowTickDurationMs = vibratorHelper.getPrimitiveDurations(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)[0] private var hasVibratedAtLowerBookend = false @@ -99,7 +100,7 @@ class SliderHapticFeedbackProvider( */ private fun vibrateDragTexture( absoluteVelocity: Float, - @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float + @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float, ) { // Check if its time to vibrate val currentTime = clock.elapsedRealtime() @@ -132,7 +133,7 @@ class SliderHapticFeedbackProvider( @VisibleForTesting fun scaleOnDragTexture( absoluteVelocity: Float, - @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float + @FloatRange(from = 0.0, to = 1.0) normalizedSliderProgress: Float, ): Float { val velocityInterpolated = velocityAccelerateInterpolator.getInterpolation( @@ -162,33 +163,24 @@ class SliderHapticFeedbackProvider( override fun onLowerBookend() { if (!hasVibratedAtLowerBookend) { - vibrateOnEdgeCollision(abs(getTrackedVelocity())) + vibrateOnEdgeCollision(abs(velocityProvider.getTrackedVelocity())) hasVibratedAtLowerBookend = true } } override fun onUpperBookend() { if (!hasVibratedAtUpperBookend) { - vibrateOnEdgeCollision(abs(getTrackedVelocity())) + vibrateOnEdgeCollision(abs(velocityProvider.getTrackedVelocity())) hasVibratedAtUpperBookend = true } } override fun onProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) { - vibrateDragTexture(abs(getTrackedVelocity()), progress) + vibrateDragTexture(abs(velocityProvider.getTrackedVelocity()), progress) hasVibratedAtUpperBookend = false hasVibratedAtLowerBookend = false } - private fun getTrackedVelocity(): Float { - velocityTracker.computeCurrentVelocity(UNITS_SECOND, config.maxVelocityToScale) - return if (velocityTracker.isAxisSupported(config.velocityAxis)) { - velocityTracker.getAxisVelocity(config.velocityAxis) - } else { - 0f - } - } - override fun onProgressJump(@FloatRange(from = 0.0, to = 1.0) progress: Float) {} override fun onSelectAndArrow(@FloatRange(from = 0.0, to = 1.0) progress: Float) {} @@ -199,6 +191,5 @@ class SliderHapticFeedbackProvider( .setUsage(VibrationAttributes.USAGE_TOUCH) .setFlags(VibrationAttributes.FLAG_PIPELINED_EFFECT) .build() - private const val UNITS_SECOND = 1000 } } diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt new file mode 100644 index 000000000000..1dbcb3dfe399 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/compose/ui/SliderHapticsViewModel.kt @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.haptics.slider.compose.ui + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.interaction.DragInteraction +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.util.VelocityTracker +import androidx.compose.ui.unit.Velocity +import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig +import com.android.systemui.haptics.slider.SliderDragVelocityProvider +import com.android.systemui.haptics.slider.SliderEventType +import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig +import com.android.systemui.haptics.slider.SliderHapticFeedbackProvider +import com.android.systemui.haptics.slider.SliderStateProducer +import com.android.systemui.haptics.slider.SliderStateTracker +import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.util.time.SystemClock +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import kotlin.math.abs +import kotlinx.coroutines.Job +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.coroutineScope + +class SliderHapticsViewModel +@AssistedInject +constructor( + @Assisted private val interactionSource: InteractionSource, + @Assisted private val sliderRange: ClosedFloatingPointRange<Float>, + @Assisted private val orientation: Orientation, + @Assisted private val sliderHapticFeedbackConfig: SliderHapticFeedbackConfig, + @Assisted private val sliderTrackerConfig: SeekableSliderTrackerConfig, + vibratorHelper: VibratorHelper, + systemClock: SystemClock, +) : ExclusiveActivatable() { + + var currentSliderEventType = SliderEventType.NOTHING + private set + + private val velocityTracker = VelocityTracker() + private val maxVelocity = + Velocity( + sliderHapticFeedbackConfig.maxVelocityToScale, + sliderHapticFeedbackConfig.maxVelocityToScale, + ) + private val dragVelocityProvider = SliderDragVelocityProvider { + val velocity = + when (orientation) { + Orientation.Horizontal -> velocityTracker.calculateVelocity(maxVelocity).x + Orientation.Vertical -> velocityTracker.calculateVelocity(maxVelocity).y + } + abs(velocity) + } + + private var startingProgress = 0f + + // Haptic slider stack of components + private val sliderStateProducer = SliderStateProducer() + private val sliderHapticFeedbackProvider = + SliderHapticFeedbackProvider( + vibratorHelper, + dragVelocityProvider, + sliderHapticFeedbackConfig, + systemClock, + ) + private var sliderTracker: SliderStateTracker? = null + + private var trackerJob: Job? = null + + val isRunning: Boolean + get() = trackerJob?.isActive == true && sliderTracker?.isTracking == true + + override suspend fun onActivated(): Nothing { + coroutineScope { + trackerJob = + launch("SliderHapticsViewModel#SliderStateTracker") { + try { + sliderTracker = + SliderStateTracker( + sliderHapticFeedbackProvider, + sliderStateProducer, + this, + sliderTrackerConfig, + ) + sliderTracker?.startTracking() + awaitCancellation() + } finally { + sliderTracker?.stopTracking() + sliderTracker = null + velocityTracker.resetTracking() + } + } + + launch("SliderHapticsViewModel#InteractionSource") { + interactionSource.interactions.collect { interaction -> + if (interaction is DragInteraction.Start) { + currentSliderEventType = SliderEventType.STARTED_TRACKING_TOUCH + sliderStateProducer.onStartTracking(true) + } + } + } + awaitCancellation() + } + } + + /** + * React to a value change in the slider. + * + * @param[value] latest value of the slider inside the [sliderRange] provided to the class + * constructor. + */ + fun onValueChange(value: Float) { + val normalized = value.normalize() + when (currentSliderEventType) { + SliderEventType.NOTHING -> { + currentSliderEventType = SliderEventType.STARTED_TRACKING_PROGRAM + startingProgress = normalized + sliderStateProducer.resetWithProgress(normalized) + sliderStateProducer.onStartTracking(false) + } + SliderEventType.STARTED_TRACKING_TOUCH -> { + startingProgress = normalized + currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER + } + SliderEventType.PROGRESS_CHANGE_BY_USER -> { + velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset()) + currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_USER + sliderStateProducer.onProgressChanged(true, normalized) + } + SliderEventType.STARTED_TRACKING_PROGRAM -> { + startingProgress = normalized + currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM + } + SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> { + velocityTracker.addPosition(System.currentTimeMillis(), normalized.toOffset()) + currentSliderEventType = SliderEventType.PROGRESS_CHANGE_BY_PROGRAM + sliderStateProducer.onProgressChanged(false, normalized) + } + else -> {} + } + } + + fun onValueChangeEnded() { + when (currentSliderEventType) { + SliderEventType.STARTED_TRACKING_PROGRAM, + SliderEventType.PROGRESS_CHANGE_BY_PROGRAM -> sliderStateProducer.onStopTracking(false) + SliderEventType.STARTED_TRACKING_TOUCH, + SliderEventType.PROGRESS_CHANGE_BY_USER -> sliderStateProducer.onStopTracking(true) + else -> {} + } + currentSliderEventType = SliderEventType.NOTHING + velocityTracker.resetTracking() + } + + private fun Float.normalize(): Float = + (this / (sliderRange.endInclusive - sliderRange.start)).coerceIn(0f, 1f) + + private fun Float.toOffset(): Offset = + when (orientation) { + Orientation.Horizontal -> Offset(x = this - startingProgress, y = 0f) + Orientation.Vertical -> Offset(x = 0f, y = this - startingProgress) + } + + @AssistedFactory + interface Factory { + fun create( + interactionSource: InteractionSource, + sliderRange: ClosedFloatingPointRange<Float>, + orientation: Orientation, + sliderHapticFeedbackConfig: SliderHapticFeedbackConfig, + sliderTrackerConfig: SeekableSliderTrackerConfig, + ): SliderHapticsViewModel + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 6df8355550a0..a94df091230d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -30,7 +30,7 @@ import android.net.Uri import android.os.Binder import android.os.Bundle import android.util.Log -import com.android.app.tracing.coroutines.runBlocking +import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking import com.android.systemui.SystemUIAppComponentFactoryBase import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback import com.android.systemui.dagger.qualifiers.Main diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 063adc834f30..3230285fcd71 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -30,8 +30,6 @@ import com.android.compose.animation.scene.SceneTransitionLayout import com.android.internal.jank.InteractionJankMonitor import com.android.keyguard.KeyguardStatusView import com.android.keyguard.KeyguardStatusViewController -import com.android.keyguard.LegacyLockIconViewController -import com.android.keyguard.LockIconView import com.android.keyguard.dagger.KeyguardStatusViewComponent import com.android.systemui.CoreStartable import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder @@ -39,7 +37,6 @@ import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.LockscreenSceneBlueprint import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder @@ -94,7 +91,6 @@ constructor( private val configuration: ConfigurationState, private val context: Context, private val keyguardIndicationController: KeyguardIndicationController, - private val lockIconViewController: Lazy<LegacyLockIconViewController>, private val shadeInteractor: ShadeInteractor, private val interactionJankMonitor: InteractionJankMonitor, private val deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor, @@ -171,10 +167,6 @@ constructor( private fun initializeViews() { val indicationArea = KeyguardIndicationArea(context, null) keyguardIndicationController.setIndicationArea(indicationArea) - - if (!DeviceEntryUdfpsRefactor.isEnabled) { - lockIconViewController.get().setLockIconView(LockIconView(context, null)) - } } private fun bindKeyguardRootView() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index d28b08f83a4e..fbc76c587be2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -139,7 +139,6 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.ui.viewmodel.DreamViewModel; import com.android.systemui.dump.DumpManager; @@ -3569,9 +3568,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } // Ensure that keyguard becomes visible if the going away animation is canceled - if (showKeyguard && !KeyguardWmStateRefactor.isEnabled() - && (MigrateClocksToBlueprint.isEnabled() - || DeviceEntryUdfpsRefactor.isEnabled())) { + if (showKeyguard && !KeyguardWmStateRefactor.isEnabled()) { mKeyguardInteractor.showKeyguard(); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 690ae71aa56e..b7d0d453779f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -24,7 +24,7 @@ import android.annotation.SuppressLint import android.os.Trace import android.util.Log import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.shared.model.KeyguardState diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt index 96260770d89f..03cf1a4b3e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt @@ -23,15 +23,12 @@ import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.BiometricType import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -41,7 +38,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch /** * Encapsulates business logic for device entry events that impact the side fingerprint sensor @@ -51,7 +47,6 @@ import kotlinx.coroutines.launch class DeviceEntrySideFpsOverlayInteractor @Inject constructor( - @Application private val applicationScope: CoroutineScope, @Application private val context: Context, deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, private val sceneInteractor: SceneInteractor, @@ -60,18 +55,6 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor, ) { - init { - if (!DeviceEntryUdfpsRefactor.isEnabled) { - applicationScope.launch { - deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType -> - if (sensorType == BiometricType.SIDE_FINGERPRINT) { - alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG) - } - } - } - } - } - private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) @@ -90,7 +73,7 @@ constructor( primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(), // Bouncer scene visibility changes. isBouncerSceneActive, - deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it } + deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it }, ) .map { isBouncerActive() && diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 4cf9ec8890d4..9896365abff9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.Log import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main 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 9a0a85823929..7b757657b1d9 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 @@ -20,7 +20,7 @@ import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.DreamManager import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index 6b6a3dce630a..a6f0db5a86aa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 58c8a0456241..606a7a988970 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index 6d1d9cbd9aae..4d3727657f3e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import android.animation.ValueAnimator import android.util.MathUtils import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Flags.communalSceneKtfRefactor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 2c3b481b9e16..26bf26b34a8a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -22,7 +22,7 @@ import android.app.admin.DevicePolicyManager import android.content.Context import android.content.Intent import android.util.Log -import com.android.app.tracing.coroutines.withContext +import com.android.app.tracing.coroutines.withContextTraced as withContext import com.android.compose.animation.scene.ObservableTransitionState import com.android.internal.widget.LockPatternUtils import com.android.keyguard.logging.KeyguardQuickAffordancesLogger diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt index fe5f632c0b6a..23b7b664d4f1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerMessageAreaViewBinder.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.AuthKeyguardMessageArea import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt index fb9719142b54..6ef9863f1112 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerUdfpsViewBinder.kt @@ -21,8 +21,7 @@ import android.content.res.ColorStateList import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel import com.android.systemui.lifecycle.repeatWhenAttached @@ -34,13 +33,7 @@ object AlternateBouncerUdfpsViewBinder { /** Updates UI for the UDFPS icon on the alternate bouncer. */ @JvmStatic - fun bind( - view: DeviceEntryIconView, - viewModel: AlternateBouncerUdfpsIconViewModel, - ) { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { - return - } + fun bind(view: DeviceEntryIconView, viewModel: AlternateBouncerUdfpsIconViewModel) { val fgIconView = view.iconView val bgView = view.bgView @@ -66,7 +59,7 @@ object AlternateBouncerUdfpsViewBinder { viewModel.fgViewModel.collect { fgViewModel -> fgIconView.setImageState( view.getIconState(fgViewModel.type, fgViewModel.useAodVariant), - /* merge */ false + /* merge */ false, ) fgIconView.imageTintList = ColorStateList.valueOf(fgViewModel.tint) fgIconView.setPadding( 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 76962732ad01..7a368999ecc9 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 @@ -29,11 +29,10 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.deviceentry.ui.binder.UdfpsAccessibilityOverlayBinder import com.android.systemui.deviceentry.ui.view.UdfpsAccessibilityOverlay import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel @@ -95,7 +94,7 @@ constructor( private var alternateBouncerView: ConstraintLayout? = null override fun start() { - if (!DeviceEntryUdfpsRefactor.isEnabled || SceneContainerFlag.isEnabled) { + if (SceneContainerFlag.isEnabled) { return } @@ -182,14 +181,7 @@ constructor( } /** Binds the view to the view-model, continuing to update the former based on the latter. */ - fun bind( - view: ConstraintLayout, - alternateBouncerDependencies: AlternateBouncerDependencies, - ) { - if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { - return - } - + fun bind(view: ConstraintLayout, alternateBouncerDependencies: AlternateBouncerDependencies) { optionallyAddUdfpsViews( view = view, logger = alternateBouncerDependencies.logger, @@ -287,10 +279,7 @@ constructor( ) } view.addView(udfpsView) - AlternateBouncerUdfpsViewBinder.bind( - udfpsView, - udfpsIconViewModel, - ) + AlternateBouncerUdfpsViewBinder.bind(udfpsView, udfpsIconViewModel) } val constraintSet = ConstraintSet().apply { clone(view) } @@ -310,17 +299,17 @@ constructor( ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, - iconLocation.left + iconLocation.left, ) // udfpsA11yOverlayView: constrainWidth( udfpsA11yOverlayViewId, - ViewGroup.LayoutParams.MATCH_PARENT + ViewGroup.LayoutParams.MATCH_PARENT, ) constrainHeight( udfpsA11yOverlayViewId, - ViewGroup.LayoutParams.MATCH_PARENT + ViewGroup.LayoutParams.MATCH_PARENT, ) } constraintSet.applyTo(view) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index b951b736abf2..6c104a0a2470 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -28,9 +28,8 @@ import androidx.compose.ui.graphics.toArgb import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.view.LongPressHandlingView -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel @@ -68,7 +67,6 @@ object DeviceEntryIconViewBinder { vibratorHelper: VibratorHelper, overrideColor: Color? = null, ): DisposableHandle { - DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode() val disposables = DisposableHandles() val longPressHandlingView = view.longPressHandlingView val fgIconView = view.iconView @@ -79,7 +77,7 @@ object DeviceEntryIconViewBinder { view: View, x: Int, y: Int, - isA11yAction: Boolean + isA11yAction: Boolean, ) { if ( !isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY) @@ -87,14 +85,11 @@ object DeviceEntryIconViewBinder { Log.d( TAG, "Long press rejected because it is not a11yAction " + - "and it is a falseLongTap" + "and it is a falseLongTap", ) return } - vibratorHelper.performHapticFeedback( - view, - HapticFeedbackConstants.CONFIRM, - ) + vibratorHelper.performHapticFeedback(view, HapticFeedbackConstants.CONFIRM) applicationScope.launch { view.clearFocus() view.clearAccessibilityFocus() @@ -192,7 +187,7 @@ object DeviceEntryIconViewBinder { fgViewModel.viewModel.collect { viewModel -> fgIconView.setImageState( view.getIconState(viewModel.type, viewModel.useAodVariant), - /* merge */ false + /* merge */ false, ) if (viewModel.type.contentDescriptionResId != -1) { fgIconView.contentDescription = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 00aa44fe795b..5bad0168fe05 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -22,7 +22,8 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition @@ -32,7 +33,6 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import com.android.systemui.util.kotlin.pairwise @@ -128,7 +128,7 @@ object KeyguardBlueprintViewBinder { ) { val currentClock = viewModel.currentClock.value if (!DEBUG || currentClock == null) return - val smallClockViewId = R.id.lockscreen_clock_view + val smallClockViewId = customR.id.lockscreen_clock_view val largeClockViewId = currentClock.largeClock.layout.views[0].id val smartspaceDateId = sharedR.id.date_smartspace_view Log.i( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 660a650fb916..3bdf7dac75b3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -36,7 +36,7 @@ import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.settingslib.Utils import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.Expandable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt index ba94f45528c7..8b947a3bcb1e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt @@ -22,7 +22,7 @@ import android.view.ViewGroup import android.widget.TextView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index 76d3389c25b4..2fd9818a38f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -22,7 +22,7 @@ import android.view.accessibility.AccessibilityNodeInfo import androidx.core.view.accessibility.AccessibilityNodeInfoCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.view.LongPressHandlingView import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 57cb10ff9367..210f4cdaaaf9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -33,9 +33,9 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.policy.SystemBarUtils -import com.android.systemui.customization.R as customizationR +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection.Companion.getDimen @@ -50,6 +50,8 @@ import kotlin.reflect.KSuspendFunction1 /** Binder for the small clock view, large clock view. */ object KeyguardPreviewClockViewBinder { + val lockId = View.generateViewId() + @JvmStatic fun bind( largeClockHostView: View, @@ -120,36 +122,36 @@ object KeyguardPreviewClockViewBinder { private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) { constraints.apply { - constrainWidth(R.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) + constrainWidth(customR.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) // The following two lines make lockscreen_clock_view_large is constrained to available // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT - constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT) - constrainMaxHeight(R.id.lockscreen_clock_view_large, 0) + constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT) + constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0) val largeClockTopMargin = SystemBarUtils.getStatusBarHeight(context) + context.resources.getDimensionPixelSize( - customizationR.dimen.small_clock_padding_top + customR.dimen.small_clock_padding_top ) + context.resources.getDimensionPixelSize( R.dimen.keyguard_smartspace_top_offset ) + getDimen(context, DATE_WEATHER_VIEW_HEIGHT) + getDimen(context, ENHANCED_SMARTSPACE_HEIGHT) - connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) - connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START) + connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) + connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START) connect( - R.id.lockscreen_clock_view_large, + customR.id.lockscreen_clock_view_large, ConstraintSet.END, PARENT_ID, ConstraintSet.END, ) - // In preview, we'll show UDFPS icon for UDFPS devices - // and nothing for non-UDFPS devices, - // but we need position of device entry icon to constrain clock - if (getConstraint(R.id.lock_icon_view) != null) { - connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.lock_icon_view, TOP) - } else { + + // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS + // devices, but we need position of device entry icon to constrain clock + if (getConstraint(lockId) != null) { + connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP) + } else { // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection val bottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) @@ -159,7 +161,7 @@ object KeyguardPreviewClockViewBinder { val lockIconRadiusPx = (defaultDensity * 36).toInt() val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx connect( - R.id.lockscreen_clock_view_large, + customR.id.lockscreen_clock_view_large, BOTTOM, PARENT_ID, BOTTOM, @@ -167,23 +169,23 @@ object KeyguardPreviewClockViewBinder { ) } - constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT) + constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT) constrainHeight( - R.id.lockscreen_clock_view, - context.resources.getDimensionPixelSize(customizationR.dimen.small_clock_height), + customR.id.lockscreen_clock_view, + context.resources.getDimensionPixelSize(customR.dimen.small_clock_height), ) connect( - R.id.lockscreen_clock_view, + customR.id.lockscreen_clock_view, START, PARENT_ID, START, - context.resources.getDimensionPixelSize(customizationR.dimen.clock_padding_start) + + context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) + context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal), ) val smallClockTopMargin = context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + Utils.getStatusBarHeaderHeightKeyguard(context) - connect(R.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin) + connect(customR.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt index 4b75b804e52b..baa681282a0b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewSmartspaceViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isInvisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel import com.android.systemui.lifecycle.repeatWhenAttached diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index 27dd18d2b24e..cfd6481234ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -30,7 +30,7 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.logging.KeyguardQuickAffordancesLogger import com.android.settingslib.Utils import com.android.systemui.animation.Expandable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index ee2ee522cd0e..89bca0c6a373 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -39,7 +39,7 @@ import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD import com.android.keyguard.AuthInteractionProperties @@ -52,8 +52,8 @@ import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.view.onApplyWindowInsets import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.common.ui.view.onTouchListener +import com.android.systemui.customization.R as customR import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.MigrateClocksToBlueprint @@ -180,16 +180,12 @@ object KeyguardRootViewBinder { } } - if ( - KeyguardBottomAreaRefactor.isEnabled || DeviceEntryUdfpsRefactor.isEnabled - ) { - launch("$TAG#alpha") { - viewModel.alpha(viewState).collect { alpha -> - view.alpha = alpha - if (KeyguardBottomAreaRefactor.isEnabled) { - childViews[statusViewId]?.alpha = alpha - childViews[burnInLayerId]?.alpha = alpha - } + launch("$TAG#alpha") { + viewModel.alpha(viewState).collect { alpha -> + view.alpha = alpha + if (KeyguardBottomAreaRefactor.isEnabled) { + childViews[statusViewId]?.alpha = alpha + childViews[burnInLayerId]?.alpha = alpha } } } @@ -223,7 +219,6 @@ object KeyguardRootViewBinder { indicationArea, startButton, endButton, - lockIcon, deviceEntryIcon -> { // Do not move these views } @@ -622,12 +617,11 @@ object KeyguardRootViewBinder { private val statusViewId = R.id.keyguard_status_view private val burnInLayerId = R.id.burn_in_layer private val aodNotificationIconContainerId = R.id.aod_notification_icon_container - private val largeClockId = R.id.lockscreen_clock_view_large - private val smallClockId = R.id.lockscreen_clock_view + private val largeClockId = customR.id.lockscreen_clock_view_large + private val smallClockId = customR.id.lockscreen_clock_view private val indicationArea = R.id.keyguard_indication_area private val startButton = R.id.start_button private val endButton = R.id.end_button - private val lockIcon = R.id.lock_icon_view private val deviceEntryIcon = R.id.device_entry_icon_view private val nsslPlaceholderId = R.id.nssl_placeholder private val authInteractionProperties = AuthInteractionProperties() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt index 4150ceb8aa31..79360370d937 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSettingsViewBinder.kt @@ -22,7 +22,7 @@ import android.view.View import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.common.ui.binder.TextViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt index 8b74f5dc791d..de4a1b03203c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSmartspaceViewBinder.kt @@ -21,7 +21,7 @@ import androidx.constraintlayout.helper.widget.Layer import androidx.constraintlayout.widget.ConstraintLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt index fd27dc39299b..3934917d13c4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardSurfaceBehindViewBinder.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.binder -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.keyguard.ui.viewmodel.KeyguardSurfaceBehindViewModel import kotlinx.coroutines.CoroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt index b2ee68967878..2df17c39d90d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.binder import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.LightRevealScrim diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt index ae46dd3a6ef3..b1ce47ee79e6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityViewBinder.kt @@ -16,7 +16,7 @@ package com.android.systemui.keyguard.ui.binder -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel import kotlinx.coroutines.CoroutineScope 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 cef9a4eaf2bd..08d35a72ab12 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 @@ -17,7 +17,6 @@ package com.android.systemui.keyguard.ui.preview -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.app.WallpaperColors import android.content.BroadcastReceiver import android.content.Context @@ -57,6 +56,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.communal.ui.binder.CommunalTutorialIndicatorViewBinder import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewModel +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -151,10 +151,7 @@ constructor( private val width: Int = bundle.getInt(KEY_VIEW_WIDTH) private val height: Int = bundle.getInt(KEY_VIEW_HEIGHT) private val shouldHighlightSelectedAffordance: Boolean = - bundle.getBoolean( - KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES, - false, - ) + bundle.getBoolean(KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES, false) private val displayId = bundle.getInt(KEY_DISPLAY_ID, DEFAULT_DISPLAY) private val display: Display? = displayManager.getDisplay(displayId) @@ -188,24 +185,26 @@ constructor( private var themeStyle: Style? = null init { - coroutineScope = CoroutineScope(applicationScope.coroutineContext + Job() + createCoroutineTracingContext("KeyguardPreviewRenderer")) + coroutineScope = + CoroutineScope( + applicationScope.coroutineContext + + Job() + + newTracingContext("KeyguardPreviewRenderer") + ) disposables += DisposableHandle { coroutineScope.cancel() } clockController.setFallbackWeatherData(WeatherData.getPlaceholderWeatherData()) if (KeyguardBottomAreaRefactor.isEnabled) { quickAffordancesCombinedViewModel.enablePreviewMode( initiallySelectedSlotId = - bundle.getString( - KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID, - ) ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, + bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID) + ?: KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, ) } else { bottomAreaViewModel.enablePreviewMode( initiallySelectedSlotId = - bundle.getString( - KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID, - ), + bundle.getString(KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID), shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance, ) } @@ -218,7 +217,7 @@ constructor( context, displayManager.getDisplay(DEFAULT_DISPLAY), if (hostToken == null) null else InputTransferToken(hostToken), - "KeyguardPreviewRenderer" + "KeyguardPreviewRenderer", ) disposables += DisposableHandle { host.release() } } @@ -247,12 +246,12 @@ constructor( rootView.measure( View.MeasureSpec.makeMeasureSpec( displayInfo?.logicalWidth ?: windowManager.currentWindowMetrics.bounds.width(), - View.MeasureSpec.EXACTLY + View.MeasureSpec.EXACTLY, ), View.MeasureSpec.makeMeasureSpec( displayInfo?.logicalHeight ?: windowManager.currentWindowMetrics.bounds.height(), - View.MeasureSpec.EXACTLY + View.MeasureSpec.EXACTLY, ), ) rootView.layout(0, 0, rootView.measuredWidth, rootView.measuredHeight) @@ -278,9 +277,7 @@ constructor( } } - fun onStartCustomizingQuickAffordances( - initiallySelectedSlotId: String?, - ) { + fun onStartCustomizingQuickAffordances(initiallySelectedSlotId: String?) { quickAffordancesCombinedViewModel.enablePreviewMode( initiallySelectedSlotId = initiallySelectedSlotId, shouldHighlightSelectedAffordance = true, @@ -379,15 +376,9 @@ constructor( @Deprecated("Deprecated as part of b/278057014") private fun setUpBottomArea(parentView: ViewGroup) { val bottomAreaView = - LayoutInflater.from(context) - .inflate( - R.layout.keyguard_bottom_area, - parentView, - false, - ) as KeyguardBottomAreaView - bottomAreaView.init( - viewModel = bottomAreaViewModel, - ) + LayoutInflater.from(context).inflate(R.layout.keyguard_bottom_area, parentView, false) + as KeyguardBottomAreaView + bottomAreaView.init(viewModel = bottomAreaViewModel) parentView.addView( bottomAreaView, FrameLayout.LayoutParams( @@ -433,7 +424,7 @@ constructor( setUpUdfps( previewContext, - if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView + if (MigrateClocksToBlueprint.isEnabled) keyguardRootView else rootView, ) if (KeyguardBottomAreaRefactor.isEnabled) { @@ -466,7 +457,7 @@ constructor( previewContext, it, previewInSplitShade(), - smartspaceViewModel + smartspaceViewModel, ) } setupCommunalTutorialIndicator(keyguardRootView) @@ -515,23 +506,20 @@ constructor( val finger = LayoutInflater.from(previewContext) - .inflate( - R.layout.udfps_keyguard_preview, - parentView, - false, - ) as View + .inflate(R.layout.udfps_keyguard_preview, parentView, false) as View // Place the UDFPS view in the proper sensor location if (MigrateClocksToBlueprint.isEnabled) { - finger.id = R.id.lock_icon_view + val lockId = KeyguardPreviewClockViewBinder.lockId + finger.id = lockId parentView.addView(finger) val cs = ConstraintSet() cs.clone(parentView as ConstraintLayout) cs.apply { - constrainWidth(R.id.lock_icon_view, sensorBounds.width()) - constrainHeight(R.id.lock_icon_view, sensorBounds.height()) - connect(R.id.lock_icon_view, TOP, PARENT_ID, TOP, sensorBounds.top) - connect(R.id.lock_icon_view, START, PARENT_ID, START, sensorBounds.left) + constrainWidth(lockId, sensorBounds.width()) + constrainHeight(lockId, sensorBounds.height()) + connect(lockId, TOP, PARENT_ID, TOP, sensorBounds.top) + connect(lockId, START, PARENT_ID, START, sensorBounds.left) } cs.applyTo(parentView) } else { @@ -541,7 +529,7 @@ constructor( sensorBounds.left, sensorBounds.top, sensorBounds.right, - sensorBounds.bottom + sensorBounds.bottom, ) parentView.addView(finger, fingerprintLayoutParams) } @@ -565,7 +553,7 @@ constructor( FrameLayout.LayoutParams.WRAP_CONTENT, resources.getDimensionPixelSize( com.android.systemui.customization.R.dimen.small_clock_height - ) + ), ) layoutParams.topMargin = SystemBarUtils.getStatusBarHeight(previewContext) + @@ -579,7 +567,7 @@ constructor( ), /* top = */ 0, /* end = */ 0, - /* bottom = */ 0 + /* bottom = */ 0, ) smallClockHostView.clipChildren = false parentView.addView(smallClockHostView) @@ -703,9 +691,7 @@ constructor( private suspend fun fetchThemeStyleFromSetting(): Style { val overlayPackageJson = withContext(backgroundDispatcher) { - secureSettings.getString( - Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES, - ) + secureSettings.getString(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES) } return if (!overlayPackageJson.isNullOrEmpty()) { try { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index 075a1d2893f7..9355200daec7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -25,7 +25,7 @@ import android.os.Messenger import android.util.ArrayMap import android.util.Log import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.runBlocking +import com.android.app.tracing.coroutines.runBlockingTraced as runBlocking import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt index d74552231209..ee4f41ddd5a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt @@ -30,7 +30,7 @@ import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.constraintlayout.widget.ConstraintSet.VISIBLE import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT -import com.android.systemui.customization.R as custR +import com.android.systemui.customization.R as customR import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor @@ -153,7 +153,7 @@ constructor( R.id.weather_clock_bc_smartspace_bottom, Barrier.BOTTOM, getDimen(ENHANCED_SMARTSPACE_HEIGHT), - (custR.id.weather_clock_time), + (customR.id.weather_clock_time), ) if ( rootViewModel.isNotifIconContainerVisible.value.value && @@ -184,40 +184,40 @@ constructor( if (keyguardClockViewModel.clockShouldBeCentered.value) PARENT_ID else R.id.split_shade_guideline constraints.apply { - connect(R.id.lockscreen_clock_view_large, START, PARENT_ID, START) - connect(R.id.lockscreen_clock_view_large, END, guideline, END) - connect(R.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP) + connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START) + connect(customR.id.lockscreen_clock_view_large, END, guideline, END) + connect(customR.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP) val largeClockTopMargin = keyguardClockViewModel.getLargeClockTopMargin() + getDimen(DATE_WEATHER_VIEW_HEIGHT) + getDimen(ENHANCED_SMARTSPACE_HEIGHT) - connect(R.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) - constrainWidth(R.id.lockscreen_clock_view_large, WRAP_CONTENT) + connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) + constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT) // The following two lines make lockscreen_clock_view_large is constrained to available // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT - constrainHeight(R.id.lockscreen_clock_view_large, WRAP_CONTENT) - constrainMaxHeight(R.id.lockscreen_clock_view_large, 0) - constrainWidth(R.id.lockscreen_clock_view, WRAP_CONTENT) + constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT) + constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0) + constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT) constrainHeight( - R.id.lockscreen_clock_view, - context.resources.getDimensionPixelSize(custR.dimen.small_clock_height), + customR.id.lockscreen_clock_view, + context.resources.getDimensionPixelSize(customR.dimen.small_clock_height), ) connect( - R.id.lockscreen_clock_view, + customR.id.lockscreen_clock_view, START, PARENT_ID, START, - context.resources.getDimensionPixelSize(custR.dimen.clock_padding_start) + + context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) + context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal), ) val smallClockTopMargin = keyguardClockViewModel.getSmallClockTopMargin() create(R.id.small_clock_guideline_top, ConstraintSet.HORIZONTAL_GUIDELINE) setGuidelineBegin(R.id.small_clock_guideline_top, smallClockTopMargin) - connect(R.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM) + connect(customR.id.lockscreen_clock_view, TOP, R.id.small_clock_guideline_top, BOTTOM) // Explicitly clear pivot to force recalculate pivot instead of using legacy value - setTransformPivot(R.id.lockscreen_clock_view_large, Float.NaN, Float.NaN) + setTransformPivot(customR.id.lockscreen_clock_view_large, Float.NaN, Float.NaN) val smallClockBottom = keyguardClockViewModel.getSmallClockTopMargin() + diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index 782d37b1929c..8d2bfb5bdf40 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -27,11 +27,8 @@ import android.view.WindowManager import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet -import com.android.keyguard.LockIconView -import com.android.keyguard.LockIconViewController import com.android.systemui.biometrics.AuthController import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardBottomAreaRefactor @@ -66,7 +63,6 @@ constructor( private val context: Context, private val notificationPanelView: NotificationPanelView, private val featureFlags: FeatureFlags, - private val lockIconViewController: Lazy<LockIconViewController>, private val deviceEntryIconViewModel: Lazy<DeviceEntryIconViewModel>, private val deviceEntryForegroundViewModel: Lazy<DeviceEntryForegroundViewModel>, private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>, @@ -78,70 +74,44 @@ constructor( private var disposableHandle: DisposableHandle? = null override fun addViews(constraintLayout: ConstraintLayout) { - if ( - !KeyguardBottomAreaRefactor.isEnabled && - !MigrateClocksToBlueprint.isEnabled && - !DeviceEntryUdfpsRefactor.isEnabled - ) { + if (!KeyguardBottomAreaRefactor.isEnabled && !MigrateClocksToBlueprint.isEnabled) { return } - notificationPanelView.findViewById<View>(R.id.lock_icon_view).let { - notificationPanelView.removeView(it) - } - val view = - if (DeviceEntryUdfpsRefactor.isEnabled) { - DeviceEntryIconView( - context, - null, - logger = - LongPressHandlingViewLogger( - logBuffer = logBuffer, - TAG - ) - ) - .apply { id = deviceEntryIconViewId } - } else { - // KeyguardBottomAreaRefactor.isEnabled or MigrateClocksToBlueprint.isEnabled - LockIconView(context, null).apply { id = R.id.lock_icon_view } - } + DeviceEntryIconView( + context, + null, + logger = LongPressHandlingViewLogger(logBuffer = logBuffer, TAG), + ) + .apply { id = deviceEntryIconViewId } + constraintLayout.addView(view) } override fun bindData(constraintLayout: ConstraintLayout) { - if (DeviceEntryUdfpsRefactor.isEnabled) { - constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let { - disposableHandle?.dispose() - disposableHandle = - DeviceEntryIconViewBinder.bind( - applicationScope, - it, - deviceEntryIconViewModel.get(), - deviceEntryForegroundViewModel.get(), - deviceEntryBackgroundViewModel.get(), - falsingManager.get(), - vibratorHelper.get(), - ) - } - } else { - constraintLayout.findViewById<LockIconView?>(R.id.lock_icon_view)?.let { - lockIconViewController.get().setLockIconView(it) - } + constraintLayout.findViewById<DeviceEntryIconView?>(deviceEntryIconViewId)?.let { + disposableHandle?.dispose() + disposableHandle = + DeviceEntryIconViewBinder.bind( + applicationScope, + it, + deviceEntryIconViewModel.get(), + deviceEntryForegroundViewModel.get(), + deviceEntryBackgroundViewModel.get(), + falsingManager.get(), + vibratorHelper.get(), + ) } } override fun applyConstraints(constraintSet: ConstraintSet) { - val isUdfpsSupported = - if (DeviceEntryUdfpsRefactor.isEnabled) { - Log.d( - "DefaultDeviceEntrySection", - "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}" - ) - deviceEntryIconViewModel.get().isUdfpsSupported.value - } else { - authController.isUdfpsSupported - } + Log.d( + "DefaultDeviceEntrySection", + "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}", + ) + val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value + val scaleFactor: Float = authController.scaleFactor val mBottomPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) @@ -160,31 +130,24 @@ constructor( val iconRadiusPx = (defaultDensity * 36).toInt() if (isUdfpsSupported) { - if (DeviceEntryUdfpsRefactor.isEnabled) { - deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation -> - Log.d( - "DeviceEntrySection", - "udfpsLocation=$udfpsLocation, " + - "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " + - "unusedAuthController=${authController.udfpsLocation}" - ) - centerIcon( - Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()), - udfpsLocation.radius, - constraintSet - ) - } - } else { - authController.udfpsLocation?.let { udfpsLocation -> - Log.d("DeviceEntrySection", "udfpsLocation=$udfpsLocation") - centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet) - } + deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation -> + Log.d( + "DeviceEntrySection", + "udfpsLocation=$udfpsLocation, " + + "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " + + "unusedAuthController=${authController.udfpsLocation}", + ) + centerIcon( + Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()), + udfpsLocation.radius, + constraintSet, + ) } } else { centerIcon( Point( (widthPixels / 2).toInt(), - (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt() + (heightPixels - ((mBottomPaddingPx + iconRadiusPx) * scaleFactor)).toInt(), ), iconRadiusPx * scaleFactor, constraintSet, @@ -193,12 +156,8 @@ constructor( } override fun removeViews(constraintLayout: ConstraintLayout) { - if (DeviceEntryUdfpsRefactor.isEnabled) { - constraintLayout.removeView(deviceEntryIconViewId) - disposableHandle?.dispose() - } else { - constraintLayout.removeView(R.id.lock_icon_view) - } + constraintLayout.removeView(deviceEntryIconViewId) + disposableHandle?.dispose() } @VisibleForTesting @@ -213,12 +172,7 @@ constructor( ) } - val iconId = - if (DeviceEntryUdfpsRefactor.isEnabled) { - deviceEntryIconViewId - } else { - R.id.lock_icon_view - } + val iconId = deviceEntryIconViewId constraintSet.apply { constrainWidth(iconId, sensorRect.right - sensorRect.left) @@ -228,14 +182,14 @@ constructor( ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP, - sensorRect.top + sensorRect.top, ) connect( iconId, ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, - sensorRect.left + sensorRect.left, ) } @@ -243,8 +197,8 @@ constructor( // Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not // updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer // being in NPVC and laying out prior to the KeyguardRootView. - // Remove when both DeviceEntryUdfpsRefactor and KeyguardBottomAreaRefactor are enabled. - if (DeviceEntryUdfpsRefactor.isEnabled && !KeyguardBottomAreaRefactor.isEnabled) { + // Remove when KeyguardBottomAreaRefactor is enabled. + if (!KeyguardBottomAreaRefactor.isEnabled) { with(notificationPanelView) { val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0 @@ -256,7 +210,7 @@ constructor( ambientLeft, sensorRect.bottom, bottomAreaViewRight - ambientLeft, - ambientTop + it.measuredHeight + ambientTop + it.measuredHeight, ) } else { // make bottom of ambient indication view the top of the lock icon @@ -264,7 +218,7 @@ constructor( ambientLeft, sensorRect.top - it.measuredHeight, bottomAreaViewRight - ambientLeft, - sensorRect.top + sensorRect.top, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt index b33d55244037..604318a2751c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/KeyguardSliceViewSection.kt @@ -22,6 +22,7 @@ import android.view.ViewGroup import androidx.constraintlayout.widget.Barrier import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.model.KeyguardSection import com.android.systemui.res.R @@ -67,7 +68,7 @@ constructor( connect( R.id.keyguard_slice_view, ConstraintSet.TOP, - R.id.lockscreen_clock_view, + customR.id.lockscreen_clock_view, ConstraintSet.BOTTOM ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt index edcf97a81ea4..620cc13a0c3a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt @@ -55,11 +55,7 @@ constructor( R.id.nssl_placeholder_barrier_bottom, Barrier.TOP, 0, - *intArrayOf( - R.id.device_entry_icon_view, - R.id.lock_icon_view, - R.id.ambient_indication_container - ) + *intArrayOf(R.id.device_entry_icon_view, R.id.ambient_indication_container), ) connect(placeHolderId, BOTTOM, R.id.nssl_placeholder_barrier_bottom, TOP) } 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 99160f8a9158..6ddcae38ce92 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 @@ -23,6 +23,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener import androidx.constraintlayout.widget.Barrier import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.customization.R as customR import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.MigrateClocksToBlueprint @@ -157,7 +158,7 @@ constructor( connect( sharedR.id.date_smartspace_view, ConstraintSet.TOP, - R.id.lockscreen_clock_view, + customR.id.lockscreen_clock_view, ConstraintSet.BOTTOM ) connect( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index 4d914c721d0c..c11005d38986 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -29,6 +29,7 @@ import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver.OnPreDrawListener import com.android.app.animation.Interpolators +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS @@ -242,11 +243,11 @@ class ClockSizeTransition( } ?: run { Log.e(TAG, "No large clock set, falling back") - addTarget(R.id.lockscreen_clock_view_large) + addTarget(customR.id.lockscreen_clock_view_large) } } else { if (DEBUG) Log.i(TAG, "Adding small clock") - addTarget(R.id.lockscreen_clock_view) + addTarget(customR.id.lockscreen_clock_view) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index 0d55709e94d6..f765e60c1c70 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.annotation.VisibleForTesting import com.android.app.tracing.FlowTracing.traceEmissionCount +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor @@ -141,6 +142,7 @@ constructor( fadeInAlpha, fadeOutAlpha, ) + .flowName("transitionAlpha") .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt index df1394bbfa5f..7c02f28c0696 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/Hydrator.kt @@ -19,7 +19,7 @@ package com.android.systemui.lifecycle import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.StateFactoryMarker -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.coroutineScope diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index 555969859a1f..a86bfb18fb70 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -17,7 +17,6 @@ package com.android.systemui.lifecycle -import android.os.Trace import android.view.View import android.view.ViewTreeObserver import androidx.annotation.MainThread @@ -25,11 +24,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.createCoroutineTracingContext -import com.android.app.tracing.coroutines.traceCoroutine -import com.android.systemui.Flags.coroutineTracing +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.util.Assert -import com.android.systemui.util.Compile import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import kotlin.coroutines.CoroutineContext @@ -83,25 +79,13 @@ fun View.repeatWhenAttached( // default behavior. Instead, we want it to run on the view's UI thread since the user will // presumably want to call view methods that require being called from said UI thread. val lifecycleCoroutineContext = MAIN_DISPATCHER_SINGLETON + coroutineContext - val traceName = - if (Compile.IS_DEBUG && coroutineTracing()) { - inferTraceSectionName() - } else { - DEFAULT_TRACE_NAME - } var lifecycleOwner: ViewLifecycleOwner? = null val onAttachListener = object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { Assert.isMainThread() lifecycleOwner?.onDestroy() - lifecycleOwner = - createLifecycleOwnerAndRun( - traceName, - view, - lifecycleCoroutineContext, - block, - ) + lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block) } override fun onViewDetachedFromWindow(v: View) { @@ -112,13 +96,7 @@ fun View.repeatWhenAttached( addOnAttachStateChangeListener(onAttachListener) if (view.isAttachedToWindow) { - lifecycleOwner = - createLifecycleOwnerAndRun( - traceName, - view, - lifecycleCoroutineContext, - block, - ) + lifecycleOwner = createLifecycleOwnerAndRun(view, lifecycleCoroutineContext, block) } return DisposableHandle { @@ -131,14 +109,15 @@ fun View.repeatWhenAttached( } private fun createLifecycleOwnerAndRun( - nameForTrace: String, view: View, coroutineContext: CoroutineContext, block: suspend LifecycleOwner.(View) -> Unit, ): ViewLifecycleOwner { return ViewLifecycleOwner(view).apply { onCreate() - lifecycleScope.launch(coroutineContext) { traceCoroutine(nameForTrace) { block(view) } } + // TODO(b/370595466): Refactor to support installing CoroutineTracingContext on the + // top-level CoroutineScope used as the lifecycleScope + lifecycleScope.launch(coroutineContext) { block(view) } } } @@ -167,9 +146,7 @@ private fun createLifecycleOwnerAndRun( * └───────────────┴───────────────────┴──────────────┴─────────────────┘ * ``` */ -class ViewLifecycleOwner( - private val view: View, -) : LifecycleOwner { +class ViewLifecycleOwner(private val view: View) : LifecycleOwner { private val windowVisibleListener = ViewTreeObserver.OnWindowVisibilityChangeListener { updateState() } @@ -205,28 +182,6 @@ class ViewLifecycleOwner( } } -private fun isFrameInteresting(frame: StackWalker.StackFrame): Boolean = - frame.className != CURRENT_CLASS_NAME && frame.className != JAVA_ADAPTER_CLASS_NAME - -/** Get a name for the trace section include the name of the call site. */ -private fun inferTraceSectionName(): String { - try { - Trace.traceBegin(Trace.TRACE_TAG_APP, "RepeatWhenAttachedKt#inferTraceSectionName") - val interestingFrame = - StackWalker.getInstance().walk { stream -> - stream.filter(::isFrameInteresting).limit(5).findFirst() - } - return if (interestingFrame.isPresent) { - val f = interestingFrame.get() - "${f.className}#${f.methodName}:${f.lineNumber} [$DEFAULT_TRACE_NAME]" - } else { - DEFAULT_TRACE_NAME - } - } finally { - Trace.traceEnd(Trace.TRACE_TAG_APP) - } -} - /** * Runs the given [block] in a new coroutine when `this` [View]'s Window's [WindowLifecycleState] is * at least at [state] (or immediately after calling this function if the window is already at least @@ -368,8 +323,7 @@ private val ViewTreeObserver.isWindowVisible * an extension function, and plumbing dagger-injected instances for static usage has little * benefit. */ -private val MAIN_DISPATCHER_SINGLETON = - Dispatchers.Main + createCoroutineTracingContext("RepeatWhenAttached") +private val MAIN_DISPATCHER_SINGLETON = Dispatchers.Main + newTracingContext("RepeatWhenAttached") private const val DEFAULT_TRACE_NAME = "repeatWhenAttached" private const val CURRENT_CLASS_NAME = "com.android.systemui.lifecycle.RepeatWhenAttachedKt" private const val JAVA_ADAPTER_CLASS_NAME = "com.android.systemui.util.kotlin.JavaAdapterKt" diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt index 544dbddeb3f0..f40ad065e5fc 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt @@ -16,13 +16,13 @@ package com.android.systemui.mediaprojection.appselector -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.app.Activity import android.content.ComponentName import android.content.Context import android.os.UserHandle import androidx.lifecycle.DefaultLifecycleObserver import com.android.launcher3.icons.IconFactory +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerLabelLoader import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader @@ -134,7 +134,11 @@ interface MediaProjectionAppSelectorModule { @MediaProjectionAppSelector @MediaProjectionAppSelectorScope fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope = - CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("MediaProjectionAppSelectorScope")) + CoroutineScope( + applicationScope.coroutineContext + + SupervisorJob() + + newTracingContext("MediaProjectionAppSelectorScope") + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt index 96386e520d5a..0166176721c3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/domain/GestureInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.navigationbar.gestural.domain -import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -35,6 +34,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 3aa9daac4866..d0f6f7961889 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -37,7 +37,7 @@ import android.os.UserManager import android.provider.Settings import android.widget.Toast import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.launch +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.dagger.qualifiers.Background 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 9c5231d716da..49b44cb95e46 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -23,7 +23,9 @@ import android.graphics.Rect import android.os.Bundle import android.util.IndentingPrintWriter import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View +import android.view.ViewConfiguration import android.view.ViewGroup import android.widget.FrameLayout import androidx.activity.OnBackPressedDispatcher @@ -35,6 +37,7 @@ import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith +import androidx.compose.foundation.ScrollState import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -43,6 +46,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect @@ -83,6 +87,7 @@ import com.android.systemui.Dumpable import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.dump.DumpManager import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.lifecycle.setSnapshotBinding import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule.QS_PANEL @@ -145,6 +150,7 @@ constructor( private val qqsVisible = MutableStateFlow(false) private val qqsPositionOnRoot = Rect() private val composeViewPositionOnScreen = Rect() + private val scrollState = ScrollState(0) // Inside object for namespacing private val notificationScrimClippingParams = @@ -210,6 +216,9 @@ constructor( context, { notificationScrimClippingParams.isEnabled }, { notificationScrimClippingParams.params.top }, + // Only allow scrolling when we are fully expanded. That way, we don't intercept + // swipes in lockscreen (when somehow QS is receiving touches). + { scrollState.canScrollForward && viewModel.expansionState.value.progress >= 1f }, ) frame.addView( composeView, @@ -488,12 +497,8 @@ constructor( private fun setListenerCollections() { lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - // TODO - // setListenerJob( - // scrollListener, - // - // ) + this@QSFragmentCompose.view?.setSnapshotBinding { + scrollListener.value?.onQsPanelScrollChanged(scrollState.value) } launch { setListenerJob( @@ -528,6 +533,7 @@ constructor( viewModel.containerViewModel.quickQuickSettingsViewModel.squishinessViewModel .squishiness .collectAsStateWithLifecycle() + Column(modifier = Modifier.sysuiResTag("quick_qs_panel")) { Box( modifier = @@ -591,7 +597,12 @@ constructor( modifier = Modifier.element(ElementKeys.QuickSettingsContent).fillMaxSize().weight(1f) ) { - Column { + DisposableEffect(Unit) { + lifecycleScope.launch { scrollState.scrollTo(0) } + onDispose { lifecycleScope.launch { scrollState.scrollTo(0) } } + } + + Column(modifier = Modifier.verticalScroll(scrollState)) { Spacer( modifier = Modifier.height { qqsPadding + qsExtraPadding.roundToPx() } ) @@ -601,15 +612,14 @@ constructor( ) } } - QuickSettingsTheme { - FooterActions( - viewModel = viewModel.footerActionsViewModel, - qsVisibilityLifecycleOwner = this@QSFragmentCompose, - modifier = - Modifier.sysuiResTag("qs_footer_actions") - .element(ElementKeys.FooterActions), - ) - } + } + QuickSettingsTheme { + FooterActions( + viewModel = viewModel.footerActionsViewModel, + qsVisibilityLifecycleOwner = this@QSFragmentCompose, + modifier = + Modifier.sysuiResTag("qs_footer_actions").element(ElementKeys.FooterActions), + ) } } } @@ -791,13 +801,17 @@ private class ExpansionTransition(currentProgress: Float) : private const val EDIT_MODE_TIME_MILLIS = 500 /** - * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as - * per [clippingEnabledProvider]. + * Performs different touch handling based on the state of the ComposeView: + * * Ignore touches below the value returned by [clippingTopProvider], when clipping is enabled, as + * per [clippingEnabledProvider]. + * * Intercept touches that would overscroll QS forward and instead allow them to be used to close + * the shade. */ private class FrameLayoutTouchPassthrough( context: Context, private val clippingEnabledProvider: () -> Boolean, private val clippingTopProvider: () -> Int, + private val canScrollForwardQs: () -> Boolean, ) : FrameLayout(context) { override fun isTransformedTouchPointInView( x: Float, @@ -811,4 +825,32 @@ private class FrameLayoutTouchPassthrough( super.isTransformedTouchPointInView(x, y, child, outLocalPoint) } } + + val touchSlop = ViewConfiguration.get(context).scaledTouchSlop + var downY = 0f + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + // If there's a touch on this view and we can scroll down, we don't want to be intercepted + val action = ev.actionMasked + + when (action) { + MotionEvent.ACTION_DOWN -> { + // If we can scroll down, make sure none of our parents intercepts us. + if (canScrollForwardQs()) { + parent?.requestDisallowInterceptTouchEvent(true) + } + downY = ev.y + } + + MotionEvent.ACTION_MOVE -> { + val y = ev.y.toInt() + val yDiff: Float = y - downY + if (yDiff < -touchSlop && !canScrollForwardQs()) { + // Intercept touches that are overscrolling. + return true + } + } + } + return super.onInterceptTouchEvent(ev) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt index 26b2e2b7bd66..424be90ba2ec 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/PaginatedGridRepository.kt @@ -38,6 +38,6 @@ constructor( ) { val rows = configurationRepository.onConfigurationChange.emitOnStart().map { - resources.getInteger(R.integer.quick_settings_max_rows) + resources.getInteger(R.integer.quick_settings_paginated_grid_num_rows) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt index f7c71ceb9e6c..ee0cfb304db0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QuickQuickSettingsRowRepository.kt @@ -34,6 +34,6 @@ constructor( ) { val rows = configurationRepository.onConfigurationChange.emitOnStart().map { - resources.getInteger(R.integer.quick_qs_panel_max_rows) + resources.getInteger(R.integer.quick_qs_paginated_grid_num_rows) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt new file mode 100644 index 000000000000..b9994d7bb821 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/BounceableInfo.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.ui.compose + +import com.android.compose.animation.Bounceable +import com.android.systemui.qs.panels.shared.model.SizedTile +import com.android.systemui.qs.panels.ui.model.GridCell +import com.android.systemui.qs.panels.ui.model.TileGridCell +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel +import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel + +data class BounceableInfo( + val bounceable: BounceableTileViewModel, + val previousTile: Bounceable?, + val nextTile: Bounceable?, + val bounceEnd: Boolean, +) + +fun List<Pair<GridCell, BounceableTileViewModel>>.bounceableInfo( + index: Int, + columns: Int, +): BounceableInfo { + val cell = this[index].first as TileGridCell + // Only look for neighbor bounceables if they are on the same row + val onLastColumn = cell.onLastColumn(cell.column, columns) + val previousTile = getOrNull(index - 1)?.takeIf { cell.column != 0 } + val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } + return BounceableInfo(this[index].second, previousTile?.second, nextTile?.second, !onLastColumn) +} + +fun List<BounceableTileViewModel>.bounceableInfo( + sizedTile: SizedTile<TileViewModel>, + index: Int, + column: Int, + columns: Int, +): BounceableInfo { + // Only look for neighbor bounceables if they are on the same row + val onLastColumn = sizedTile.onLastColumn(column, columns) + val previousTile = getOrNull(index - 1)?.takeIf { column != 0 } + val nextTile = getOrNull(index + 1)?.takeIf { !onLastColumn } + return BounceableInfo(this[index], previousTile, nextTile, !onLastColumn) +} + +private fun <T> SizedTile<T>.onLastColumn(column: Int, columns: Int): Boolean { + return column == columns - width +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt index 770fd785723a..74fa0fef21d7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt @@ -116,9 +116,9 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c regenerateGrid() _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell) } else { - // Add the tile with a temporary row which will get reassigned when + // Add the tile with a temporary row/col which will get reassigned when // regenerating spacers - _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0)) + _tiles.add(insertionIndex.coerceIn(0, _tiles.size), TileGridCell(draggedTile, 0, 0)) } regenerateGrid() 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 e749475479d8..d55763aaeddb 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 @@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.ui.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons @@ -97,7 +97,9 @@ constructor( TileGrid(tiles = page, modifier = Modifier, editModeStart = {}) } } - Box(modifier = Modifier.height(FooterHeight).fillMaxWidth()) { + // Use requiredHeight so it won't be squished if the view doesn't quite fit. As this is + // expected to be inside a scrollable container, this should not be an issue. + Box(modifier = Modifier.requiredHeight(FooterHeight).fillMaxWidth()) { PagerDots( pagerState = pagerState, activeColor = MaterialTheme.colorScheme.primary, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt index a645b51404e7..1c7a334d3ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt @@ -20,6 +20,8 @@ import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.util.fastMap @@ -29,6 +31,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.grid.ui.compose.VerticalSpannedGrid import com.android.systemui.qs.composefragment.ui.GridAnchor import com.android.systemui.qs.panels.ui.compose.infinitegrid.Tile +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey import com.android.systemui.res.R @@ -38,10 +41,11 @@ fun SceneScope.QuickQuickSettings( viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier, ) { - val sizedTiles by - viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList()) + val sizedTiles by viewModel.tileViewModels.collectAsStateWithLifecycle() val tiles = sizedTiles.fastMap { it.tile } + val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() DisposableEffect(tiles) { val token = Any() @@ -49,6 +53,7 @@ fun SceneScope.QuickQuickSettings( onDispose { tiles.forEach { it.stopListening(token) } } } val columns by viewModel.columns.collectAsStateWithLifecycle() + var cellIndex = 0 Box(modifier = modifier) { GridAnchor() VerticalSpannedGrid( @@ -59,11 +64,15 @@ fun SceneScope.QuickQuickSettings( modifier = Modifier.sysuiResTag("qqs_tile_layout"), ) { spanIndex -> val it = sizedTiles[spanIndex] + val column = cellIndex % columns + cellIndex += it.width Tile( tile = it.tile, iconOnly = it.isIcon, modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + coroutineScope = scope, + bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index 9ec5a82a18d7..71fa0ac30fb7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -31,6 +31,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -54,6 +55,7 @@ import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.toggleableState +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.android.compose.modifiers.background import com.android.compose.modifiers.thenIf @@ -64,7 +66,6 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState import com.android.systemui.res.R -import kotlinx.coroutines.delay private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target" @@ -138,13 +139,20 @@ fun LargeTileLabels( accessibilityUiState: AccessibilityUiState? = null, ) { Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) { - Text(label, color = colors.label, modifier = Modifier.tileMarquee()) + Text( + label, + style = MaterialTheme.typography.labelLarge, + color = colors.label, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) if (!TextUtils.isEmpty(secondaryLabel)) { Text( secondaryLabel ?: "", color = colors.secondaryLabel, + style = MaterialTheme.typography.bodyMedium, modifier = - Modifier.tileMarquee().thenIf( + Modifier.thenIf( accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") == true ) { @@ -182,10 +190,7 @@ fun SmallTileContent( rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true) } else { var atEnd by remember(icon.res) { mutableStateOf(false) } - LaunchedEffect(key1 = icon.res) { - delay(350) - atEnd = true - } + LaunchedEffect(key1 = icon.res) { atEnd = true } rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd) } } 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 45c1e48840ee..5c2a2bd2b78c 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 @@ -22,13 +22,13 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.animateIntAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.LocalOverscrollConfiguration import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -46,7 +46,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyGridItemScope import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.rememberScrollState @@ -66,19 +65,17 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.BiasAlignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot @@ -98,23 +95,26 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.util.fastMap -import androidx.compose.ui.zIndex +import com.android.compose.animation.bounceable import com.android.compose.modifiers.background import com.android.compose.modifiers.height import com.android.systemui.common.ui.compose.load import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl +import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.DragAndDropState import com.android.systemui.qs.panels.ui.compose.EditTileListState +import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileArrangementPadding +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.TileHeight import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.ToggleTargetSize import com.android.systemui.qs.panels.ui.compose.infinitegrid.EditModeTileDefaults.CurrentTilesGridPadding import com.android.systemui.qs.panels.ui.compose.selection.MutableSelectionState -import com.android.systemui.qs.panels.ui.compose.selection.ResizingHandle +import com.android.systemui.qs.panels.ui.compose.selection.ResizableTileContainer import com.android.systemui.qs.panels.ui.compose.selection.TileWidths import com.android.systemui.qs.panels.ui.compose.selection.clearSelectionTile import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState @@ -122,11 +122,14 @@ import com.android.systemui.qs.panels.ui.compose.selection.selectableTile import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.SpacerGridCell import com.android.systemui.qs.panels.ui.model.TileGridCell +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.groupAndSort import com.android.systemui.res.R +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.launch object TileType @@ -241,15 +244,20 @@ private fun CurrentTilesGrid( onSetTiles: (List<TileSpec>) -> Unit, ) { val currentListState by rememberUpdatedState(listState) - val tileHeight = CommonTileDefaults.TileHeight val totalRows = listState.tiles.lastOrNull()?.row ?: 0 val totalHeight by animateDpAsState( - gridHeight(totalRows + 1, tileHeight, TileArrangementPadding, CurrentTilesGridPadding), + gridHeight(totalRows + 1, TileHeight, TileArrangementPadding, CurrentTilesGridPadding), label = "QSEditCurrentTilesGridHeight", ) val gridState = rememberLazyGridState() var gridContentOffset by remember { mutableStateOf(Offset(0f, 0f)) } + val coroutineScope = rememberCoroutineScope() + + val cells = + remember(listState.tiles) { + listState.tiles.fastMap { Pair(it, BounceableTileViewModel()) } + } TileLazyGrid( state = gridState, @@ -272,7 +280,7 @@ private fun CurrentTilesGrid( } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles(listState.tiles, listState, selectionState) { spec -> + EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec -> // Toggle the current size of the tile currentListState.isIcon(spec)?.let { onResize(spec, !it) } } @@ -286,10 +294,10 @@ private fun AvailableTileGrid( columns: Int, dragAndDropState: DragAndDropState, ) { - // Available tiles aren't visible during drag and drop, so the row isn't needed + // Available tiles aren't visible during drag and drop, so the row/col isn't needed val groupedTiles = remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) { - groupAndSort(tiles.fastMap { TileGridCell(it, 0) }) + groupAndSort(tiles.fastMap { TileGridCell(it, 0, 0) }) } val labelColors = EditModeTileDefaults.editTileColors() @@ -349,24 +357,26 @@ private fun GridCell.key(index: Int, dragAndDropState: DragAndDropState): Any { /** * Adds a list of [GridCell] to the lazy grid * - * @param cells the list of [GridCell] + * @param cells the pairs of [GridCell] to [BounceableTileViewModel] * @param dragAndDropState the [DragAndDropState] for this grid * @param selectionState the [MutableSelectionState] for this grid * @param onToggleSize the callback when a tile's size is toggled */ fun LazyGridScope.EditTiles( - cells: List<GridCell>, + cells: List<Pair<GridCell, BounceableTileViewModel>>, + columns: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, + coroutineScope: CoroutineScope, onToggleSize: (spec: TileSpec) -> Unit, ) { items( count = cells.size, - key = { cells[it].key(it, dragAndDropState) }, - span = { cells[it].span }, + key = { cells[it].first.key(it, dragAndDropState) }, + span = { cells[it].first.span }, contentType = { TileType }, ) { index -> - when (val cell = cells[index]) { + when (val cell = cells[index].first) { is TileGridCell -> if (dragAndDropState.isMoving(cell.tile.tileSpec)) { // If the tile is being moved, replace it with a visible spacer @@ -385,6 +395,9 @@ fun LazyGridScope.EditTiles( dragAndDropState = dragAndDropState, selectionState = selectionState, onToggleSize = onToggleSize, + coroutineScope = coroutineScope, + bounceableInfo = cells.bounceableInfo(index, columns), + modifier = Modifier.animateItem(), ) } is SpacerGridCell -> SpacerGridCell() @@ -393,12 +406,15 @@ fun LazyGridScope.EditTiles( } @Composable -private fun LazyGridItemScope.TileGridCell( +private fun TileGridCell( cell: TileGridCell, index: Int, dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, onToggleSize: (spec: TileSpec) -> Unit, + coroutineScope: CoroutineScope, + bounceableInfo: BounceableInfo, + modifier: Modifier = Modifier, ) { val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1) var selected by remember { mutableStateOf(false) } @@ -407,6 +423,9 @@ private fun LazyGridItemScope.TileGridCell( targetValue = if (selected) 1f else 0f, label = "QSEditTileSelectionAlpha", ) + val selectionColor = MaterialTheme.colorScheme.primary + val colors = EditModeTileDefaults.editTileColors() + val currentBounceableInfo by rememberUpdatedState(bounceableInfo) LaunchedEffect(selectionState.selection?.tileSpec) { selectionState.selection?.let { @@ -420,152 +439,61 @@ private fun LazyGridItemScope.TileGridCell( selected = selectionState.selection?.tileSpec == cell.tile.tileSpec } - val modifier = - Modifier.animateItem() - .semantics(mergeDescendants = true) { - this.stateDescription = stateDescription - contentDescription = cell.tile.label.text - customActions = - listOf( - // TODO(b/367748260): Add final accessibility actions - CustomAccessibilityAction("Toggle size") { - onToggleSize(cell.tile.tileSpec) - true - } - ) - } - .height(CommonTileDefaults.TileHeight) - .fillMaxWidth() - - val content = - @Composable { - EditTile( - tileViewModel = cell.tile, - iconOnly = cell.isIcon, - selectionAlpha = { selectionAlpha }, - modifier = - Modifier.fillMaxSize() - .selectableTile(cell.tile.tileSpec, selectionState) - .dragAndDropTileSource( - SizedTileImpl(cell.tile, cell.width), - dragAndDropState, - selectionState::unSelect, - ), - ) - } - - if (selected) { - SelectedTile( - isIcon = cell.isIcon, - selectionAlpha = { selectionAlpha }, - selectionState = selectionState, - modifier = modifier.zIndex(2f), // 2f to display this tile over neighbors when dragged - content = content, - ) - } else { - UnselectedTile( - selectionAlpha = { selectionAlpha }, - selectionState = selectionState, - modifier = modifier, - content = content, - ) - } -} - -@Composable -private fun SelectedTile( - isIcon: Boolean, - selectionAlpha: () -> Float, - selectionState: MutableSelectionState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { // Current base, min and max width of this tile var tileWidths: TileWidths? by remember { mutableStateOf(null) } - - // Animated diff between the current width and the resized width of the tile. We can't use - // animateContentSize here as the tile is sometimes unbounded. - val remainingOffset by - animateIntAsState( - selectionState.resizingState?.let { tileWidths?.base?.minus(it.width) ?: 0 } ?: 0, - label = "QSEditTileWidthOffset", - ) - val padding = with(LocalDensity.current) { TileArrangementPadding.roundToPx() } - Box( - modifier.onSizeChanged { - val min = if (isIcon) it.width else (it.width - padding) / 2 - val max = if (isIcon) (it.width * 2) + padding else it.width - tileWidths = TileWidths(it.width, min, max) - } + + ResizableTileContainer( + selected = selected, + selectionState = selectionState, + selectionAlpha = { selectionAlpha }, + selectionColor = selectionColor, + tileWidths = { tileWidths }, + modifier = + modifier + .height(TileHeight) + .fillMaxWidth() + .onSizeChanged { + // Grab the size before the bounceable to get the idle width + val min = if (cell.isIcon) it.width else (it.width - padding) / 2 + val max = if (cell.isIcon) (it.width * 2) + padding else it.width + tileWidths = TileWidths(it.width, min, max) + } + .bounceable( + bounceable = currentBounceableInfo.bounceable, + previousBounceable = currentBounceableInfo.previousTile, + nextBounceable = currentBounceableInfo.nextTile, + orientation = Orientation.Horizontal, + bounceEnd = currentBounceableInfo.bounceEnd, + ), ) { - val handle = - @Composable { - ResizingHandle( - enabled = true, - selectionState = selectionState, - transition = selectionAlpha, - tileWidths = { tileWidths }, + Box( + modifier + .fillMaxSize() + .semantics(mergeDescendants = true) { + this.stateDescription = stateDescription + contentDescription = cell.tile.label.text + customActions = + listOf( + // TODO(b/367748260): Add final accessibility actions + CustomAccessibilityAction("Toggle size") { + onToggleSize(cell.tile.tileSpec) + true + } + ) + } + .selectableTile(cell.tile.tileSpec, selectionState) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } + .dragAndDropTileSource( + SizedTileImpl(cell.tile, cell.width), + dragAndDropState, + selectionState::unSelect, ) - } - - Layout(contents = listOf(content, handle)) { - (contentMeasurables, handleMeasurables), - constraints -> - // Grab the width from the resizing state if a resize is in progress, otherwise fill the - // max width - val width = - selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) - val contentPlaceable = - contentMeasurables.first().measure(constraints.copy(maxWidth = width)) - val handlePlaceable = handleMeasurables.first().measure(constraints) - - // Place the dot vertically centered on the right edge - val handleX = contentPlaceable.width - (handlePlaceable.width / 2) - val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2) - - layout(constraints.maxWidth, constraints.maxHeight) { - contentPlaceable.place(0, 0) - handlePlaceable.place(handleX, handleY) - } - } - } -} - -@Composable -private fun UnselectedTile( - selectionAlpha: () -> Float, - selectionState: MutableSelectionState, - modifier: Modifier = Modifier, - content: @Composable () -> Unit, -) { - val handle = - @Composable { - ResizingHandle( - enabled = false, - selectionState = selectionState, - transition = selectionAlpha, - ) - } - - Box(modifier) { - Layout(contents = listOf(content, handle)) { - (contentMeasurables, handleMeasurables), - constraints -> - val contentPlaceable = - contentMeasurables - .first() - .measure(constraints.copy(maxWidth = constraints.maxWidth)) - val handlePlaceable = handleMeasurables.first().measure(constraints) - - // Place the dot vertically centered on the right edge - val handleX = contentPlaceable.width - (handlePlaceable.width / 2) - val handleY = (contentPlaceable.height / 2) - (handlePlaceable.height / 2) - - layout(constraints.maxWidth, constraints.maxHeight) { - contentPlaceable.place(0, 0) - handlePlaceable.place(handleX, handleY) - } + .tileBackground(colors.background) + .tilePadding() + ) { + EditTile(tile = cell.tile, iconOnly = cell.isIcon) } } } @@ -588,19 +516,19 @@ private fun AvailableTileGridCell( verticalArrangement = spacedBy(CommonTileDefaults.TilePadding, Alignment.Top), modifier = modifier, ) { - EditTileContainer( - colors = colors, - modifier = - Modifier.fillMaxWidth() - .height(CommonTileDefaults.TileHeight) - .clearSelectionTile(selectionState) - .semantics(mergeDescendants = true) { - onClick(onClickActionName) { false } - this.stateDescription = stateDescription - } - .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) { - selectionState.unSelect() - }, + Box( + Modifier.fillMaxWidth() + .height(TileHeight) + .clearSelectionTile(selectionState) + .semantics(mergeDescendants = true) { + onClick(onClickActionName) { false } + this.stateDescription = stateDescription + } + .dragAndDropTileSource(SizedTileImpl(cell.tile, cell.width), dragAndDropState) { + selectionState.unSelect() + } + .tileBackground(colors.background) + .tilePadding() ) { // Icon SmallTileContent( @@ -626,16 +554,14 @@ private fun AvailableTileGridCell( @Composable private fun SpacerGridCell(modifier: Modifier = Modifier) { // By default, spacers are invisible and exist purely to catch drag movements - Box(modifier.height(CommonTileDefaults.TileHeight).fillMaxWidth()) + Box(modifier.height(TileHeight).fillMaxWidth()) } @Composable -fun EditTile( - tileViewModel: EditTileViewModel, +fun BoxScope.EditTile( + tile: EditTileViewModel, iconOnly: Boolean, - modifier: Modifier = Modifier, colors: TileColors = EditModeTileDefaults.editTileColors(), - selectionAlpha: () -> Float = { 1f }, ) { // Animated horizontal alignment from center (0f) to start (-1f) val alignmentValue by @@ -646,68 +572,36 @@ fun EditTile( val alignment by remember { derivedStateOf { BiasAlignment(horizontalBias = alignmentValue, verticalBias = 0f) } } + // Icon + Box(Modifier.size(ToggleTargetSize).align(alignment)) { + SmallTileContent( + icon = tile.icon, + color = colors.icon, + animateToEnd = true, + modifier = Modifier.align(Alignment.Center), + ) + } - EditTileContainer(colors = colors, selectionAlpha = selectionAlpha, modifier = modifier) { - // Icon - Box(Modifier.size(ToggleTargetSize).align(alignment)) { - SmallTileContent( - icon = tileViewModel.icon, - color = colors.icon, - animateToEnd = true, - modifier = Modifier.align(Alignment.Center), - ) - } - - // Labels, positioned after the icon - AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) { - LargeTileLabels( - label = tileViewModel.label.text, - secondaryLabel = tileViewModel.appName?.text, - colors = colors, - modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding), - ) - } + // Labels, positioned after the icon + AnimatedVisibility(visible = !iconOnly, enter = fadeIn(), exit = fadeOut()) { + LargeTileLabels( + label = tile.label.text, + secondaryLabel = tile.appName?.text, + colors = colors, + modifier = Modifier.padding(start = ToggleTargetSize + TileArrangementPadding), + ) } } -@Composable -private fun EditTileContainer( - colors: TileColors, - modifier: Modifier = Modifier, - selectionAlpha: () -> Float = { 0f }, - selectionColor: Color = MaterialTheme.colorScheme.primary, - content: @Composable BoxScope.() -> Unit = {}, -) { - Box( - Modifier.wrapContentSize().drawWithContent { - drawContent() - drawRoundRect( - SolidColor(selectionColor), - cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - style = Stroke(EditModeTileDefaults.SelectedBorderWidth.toPx()), - alpha = selectionAlpha(), - ) - } - ) { - Box( - modifier = - modifier - .drawBehind { - drawRoundRect( - SolidColor(colors.background), - cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), - ) - } - .tilePadding(), - content = content, - ) +private fun Modifier.tileBackground(color: Color): Modifier { + return drawBehind { + drawRoundRect(SolidColor(color), cornerRadius = CornerRadius(InactiveCornerRadius.toPx())) } } private object EditModeTileDefaults { const val PLACEHOLDER_ALPHA = .3f val EditGridHeaderHeight = 60.dp - val SelectedBorderWidth = 2.dp val CurrentTilesGridPadding = 8.dp @Composable diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 6920e498bdde..e5c213519415 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.util.fastMap @@ -29,7 +30,9 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.grid.ui.compose.VerticalSpannedGrid import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.PaginatableGridLayout +import com.android.systemui.qs.panels.ui.compose.bounceableInfo import com.android.systemui.qs.panels.ui.compose.rememberEditListState +import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.QSColumnsViewModel @@ -62,7 +65,11 @@ constructor( } val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle() val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) } + val bounceables = + remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by squishinessViewModel.squishiness.collectAsStateWithLifecycle() + val scope = rememberCoroutineScope() + var cellIndex = 0 VerticalSpannedGrid( columns = columns, @@ -71,11 +78,15 @@ constructor( spans = sizedTiles.fastMap { it.width }, ) { spanIndex -> val it = sizedTiles[spanIndex] + val column = cellIndex % columns + cellIndex += it.width Tile( tile = it.tile, iconOnly = iconTilesViewModel.isIconTile(it.tile.spec), modifier = Modifier.element(it.tile.spec.toElementKey(spanIndex)), squishiness = { squishiness }, + coroutineScope = scope, + bounceableInfo = bounceables.bounceableInfo(it, spanIndex, column, columns), ) } } 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 4bd5b2d68c4c..52d526123430 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 @@ -23,8 +23,8 @@ import android.service.quicksettings.Tile.STATE_ACTIVE import android.service.quicksettings.Tile.STATE_INACTIVE import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.basicMarquee import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box @@ -44,6 +44,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -61,11 +62,13 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable +import com.android.compose.animation.bounceable import com.android.compose.modifiers.thenIf import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.qs.panels.ui.compose.BounceableInfo import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.TileUiState @@ -74,6 +77,8 @@ import com.android.systemui.qs.panels.ui.viewmodel.toUiState import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.res.R import java.util.function.Supplier +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch private const val TEST_TAG_SMALL = "qs_tile_small" private const val TEST_TAG_LARGE = "qs_tile_large" @@ -102,9 +107,12 @@ fun Tile( tile: TileViewModel, iconOnly: Boolean, squishiness: () -> Float, + coroutineScope: CoroutineScope, + bounceableInfo: BounceableInfo, modifier: Modifier = Modifier, ) { val state by tile.state.collectAsStateWithLifecycle(tile.currentState) + val currentBounceableInfo by rememberUpdatedState(bounceableInfo) val resources = resources() val uiState = remember(state, resources) { state.toUiState(resources) } val colors = TileDefaults.getColorForState(uiState) @@ -112,7 +120,7 @@ fun Tile( // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) - TileContainer( + TileExpandable( color = if (iconOnly || !uiState.handlesSecondaryClick) { colors.iconBackground @@ -120,93 +128,101 @@ fun Tile( colors.background }, shape = tileShape, - iconOnly = iconOnly, - onClick = tile::onClick, - onLongClick = tile::onLongClick, - uiState = uiState, squishiness = squishiness, - modifier = modifier, + modifier = + modifier + .fillMaxWidth() + .bounceable( + bounceable = currentBounceableInfo.bounceable, + previousBounceable = currentBounceableInfo.previousTile, + nextBounceable = currentBounceableInfo.nextTile, + orientation = Orientation.Horizontal, + bounceEnd = currentBounceableInfo.bounceEnd, + ), ) { expandable -> - val icon = getTileIcon(icon = uiState.icon) - if (iconOnly) { - SmallTileContent( - icon = icon, - color = colors.icon, - modifier = Modifier.align(Alignment.Center), - ) - } else { - val iconShape = TileDefaults.animateIconShape(uiState.state) - LargeTileContent( - label = uiState.label, - secondaryLabel = uiState.secondaryLabel, - icon = icon, - colors = colors, - iconShape = iconShape, - toggleClickSupported = state.handlesSecondaryClick, - onClick = { - if (state.handlesSecondaryClick) { - tile.onSecondaryClick() - } - }, - onLongClick = { tile.onLongClick(expandable) }, - accessibilityUiState = uiState.accessibilityUiState, - squishiness = squishiness, - ) + TileContainer( + onClick = { + tile.onClick(expandable) + if (uiState.accessibilityUiState.toggleableState != null) { + coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } + } + }, + onLongClick = { tile.onLongClick(expandable) }, + uiState = uiState, + iconOnly = iconOnly, + ) { + val icon = getTileIcon(icon = uiState.icon) + if (iconOnly) { + SmallTileContent( + icon = icon, + color = colors.icon, + modifier = Modifier.align(Alignment.Center), + ) + } else { + val iconShape = TileDefaults.animateIconShape(uiState.state) + LargeTileContent( + label = uiState.label, + secondaryLabel = uiState.secondaryLabel, + icon = icon, + colors = colors, + iconShape = iconShape, + toggleClickSupported = state.handlesSecondaryClick, + onClick = { + if (state.handlesSecondaryClick) { + tile.onSecondaryClick() + } + }, + onLongClick = { tile.onLongClick(expandable) }, + accessibilityUiState = uiState.accessibilityUiState, + squishiness = squishiness, + ) + } } } } @Composable -private fun TileContainer( +private fun TileExpandable( color: Color, shape: Shape, - iconOnly: Boolean, - uiState: TileUiState, squishiness: () -> Float, modifier: Modifier = Modifier, - onClick: (Expandable) -> Unit = {}, - onLongClick: (Expandable) -> Unit = {}, - content: @Composable BoxScope.(Expandable) -> Unit, + content: @Composable (Expandable) -> Unit, ) { Expandable( color = color, shape = shape, modifier = modifier.clip(shape).verticalSquish(squishiness), ) { - val longPressLabel = longPressLabel() - Box( - modifier = - Modifier.height(CommonTileDefaults.TileHeight) - .fillMaxWidth() - .combinedClickable( - onClick = { onClick(it) }, - onLongClick = { onLongClick(it) }, - onClickLabel = uiState.accessibilityUiState.clickLabel, - onLongClickLabel = longPressLabel, - ) - .semantics { - role = uiState.accessibilityUiState.accessibilityRole - if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) { - uiState.accessibilityUiState.toggleableState?.let { - toggleableState = it - } - } - stateDescription = uiState.accessibilityUiState.stateDescription - } - .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE) - .thenIf(iconOnly) { - Modifier.semantics { - contentDescription = uiState.accessibilityUiState.contentDescription - } - } - .tilePadding() - ) { - content(it) - } + content(it) } } @Composable +fun TileContainer( + onClick: () -> Unit, + onLongClick: () -> Unit, + uiState: TileUiState, + iconOnly: Boolean, + content: @Composable BoxScope.() -> Unit, +) { + Box( + modifier = + Modifier.height(CommonTileDefaults.TileHeight) + .fillMaxWidth() + .tileCombinedClickable( + onClick = onClick, + onLongClick = onLongClick, + uiState = uiState, + iconOnly = iconOnly, + ) + .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE) + .tilePadding(), + content = content, + ) +} + +@Composable private fun getTileIcon(icon: Supplier<QSTile.Icon?>): Icon { val context = LocalContext.current return icon.get()?.let { @@ -222,14 +238,38 @@ fun tileHorizontalArrangement(): Arrangement.Horizontal { return spacedBy(space = CommonTileDefaults.TileArrangementPadding, alignment = Alignment.Start) } -fun Modifier.tileMarquee(): Modifier { - return basicMarquee(iterations = 1, initialDelayMillis = 200) -} - fun Modifier.tilePadding(): Modifier { return padding(CommonTileDefaults.TilePadding) } +@Composable +fun Modifier.tileCombinedClickable( + onClick: () -> Unit, + onLongClick: () -> Unit, + uiState: TileUiState, + iconOnly: Boolean, +): Modifier { + val longPressLabel = longPressLabel() + return combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + onClickLabel = uiState.accessibilityUiState.clickLabel, + onLongClickLabel = longPressLabel, + ) + .semantics { + role = uiState.accessibilityUiState.accessibilityRole + if (uiState.accessibilityUiState.accessibilityRole == Role.Switch) { + uiState.accessibilityUiState.toggleableState?.let { toggleableState = it } + } + stateDescription = uiState.accessibilityUiState.stateDescription + } + .thenIf(iconOnly) { + Modifier.semantics { + contentDescription = uiState.accessibilityUiState.contentDescription + } + } +} + data class TileColors( val background: Color, val iconBackground: Color, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt index 441d96289d86..1d36aee4eb85 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt @@ -89,8 +89,11 @@ class MutableSelectionState( * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as * they can be selected. */ -@Composable -fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier { +fun Modifier.selectableTile( + tileSpec: TileSpec, + selectionState: MutableSelectionState, + onClick: () -> Unit = {}, +): Modifier { return pointerInput(Unit) { detectTapGestures( onTap = { @@ -99,6 +102,7 @@ fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelection } else { selectionState.select(tileSpec, manual = true) } + onClick() } ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index e0f0b6aa8919..9f13a3788f53 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -16,63 +16,117 @@ package com.android.systemui.qs.panels.ui.compose.selection +import androidx.compose.animation.core.animateIntAsState import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectHorizontalDragGestures import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.size import androidx.compose.foundation.systemGestureExclusion import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.toSize +import androidx.compose.ui.zIndex +import com.android.compose.modifiers.thenIf +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.InactiveCornerRadius import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingDotSize +import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth /** - * Dot handling resizing drag events. Use this on the selected tile to resize it + * Places a dot to handle resizing drag events. Use this on tiles to resize. * - * @param enabled whether resizing drag events should be handled + * The dot is placed vertically centered on the right border. The [content] will have a border when + * selected. + * + * @param selected whether resizing drag events should be handled * @param selectionState the [MutableSelectionState] on the grid - * @param transition the animated value for the dot, used for its alpha and scale + * @param selectionAlpha the animated value for the dot and border alpha + * @param selectionColor the [Color] of the dot and border * @param tileWidths the [TileWidths] of the selected tile - * @param onResize the callback when the drag passes the resizing threshold */ @Composable -fun ResizingHandle( +fun ResizableTileContainer( + selected: Boolean, + selectionState: MutableSelectionState, + selectionAlpha: () -> Float, + selectionColor: Color, + tileWidths: () -> TileWidths?, + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit = {}, +) { + Box( + modifier + .resizable(selected, selectionState, tileWidths) + .selectionBorder(selectionColor, selectionAlpha) + ) { + content() + ResizingHandle( + enabled = selected, + selectionState = selectionState, + transition = selectionAlpha, + tileWidths = tileWidths, + modifier = + // Higher zIndex to make sure the handle is drawn above the content + Modifier.zIndex(2f), + ) + } +} + +@Composable +private fun ResizingHandle( enabled: Boolean, selectionState: MutableSelectionState, transition: () -> Float, - tileWidths: () -> TileWidths? = { null }, + tileWidths: () -> TileWidths?, + modifier: Modifier = Modifier, ) { - if (enabled) { - // Manually creating the touch target around the resizing dot to ensure that the next tile - // does - // not receive the touch input accidentally. - val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current - Box( - Modifier.size(minTouchTargetSize) - .systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } - .pointerInput(Unit) { - detectHorizontalDragGestures( - onHorizontalDrag = { _, offset -> selectionState.onResizingDrag(offset) }, - onDragStart = { - tileWidths()?.let { selectionState.onResizingDragStart(it) } - }, - onDragEnd = selectionState::onResizingDragEnd, - onDragCancel = selectionState::onResizingDragEnd, + // Manually creating the touch target around the resizing dot to ensure that the next tile + // does not receive the touch input accidentally. + val minTouchTargetSize = LocalMinimumInteractiveComponentSize.current + Box( + modifier + .layout { measurable, constraints -> + val size = minTouchTargetSize.roundToPx() + val placeable = measurable.measure(Constraints(size, size, size, size)) + layout(placeable.width, placeable.height) { + placeable.place( + x = constraints.maxWidth - placeable.width / 2, + y = constraints.maxHeight / 2 - placeable.height / 2, ) } - ) { - ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center)) - } - } else { - ResizingDot(transition = transition) + } + .thenIf(enabled) { + Modifier.systemGestureExclusion { Rect(Offset.Zero, it.size.toSize()) } + .pointerInput(Unit) { + detectHorizontalDragGestures( + onHorizontalDrag = { _, offset -> + selectionState.onResizingDrag(offset) + }, + onDragStart = { + tileWidths()?.let { selectionState.onResizingDragStart(it) } + }, + onDragEnd = selectionState::onResizingDragEnd, + onDragCancel = selectionState::onResizingDragEnd, + ) + } + } + ) { + ResizingDot(transition = transition, modifier = Modifier.align(Alignment.Center)) } } @@ -88,6 +142,45 @@ private fun ResizingDot( } } +private fun Modifier.selectionBorder( + selectionColor: Color, + selectionAlpha: () -> Float = { 0f }, +): Modifier { + return drawWithContent { + drawContent() + drawRoundRect( + SolidColor(selectionColor), + cornerRadius = CornerRadius(InactiveCornerRadius.toPx()), + style = Stroke(SelectedBorderWidth.toPx()), + alpha = selectionAlpha(), + ) + } +} + +@Composable +private fun Modifier.resizable( + selected: Boolean, + selectionState: MutableSelectionState, + tileWidths: () -> TileWidths?, +): Modifier { + if (!selected) return zIndex(1f) + + // Animated diff between the current width and the resized width of the tile. We can't use + // animateContentSize here as the tile is sometimes unbounded. + val remainingOffset by + animateIntAsState( + selectionState.resizingState?.let { tileWidths()?.base?.minus(it.width) ?: 0 } ?: 0, + label = "QSEditTileWidthOffset", + ) + return zIndex(2f).layout { measurable, constraints -> + // Grab the width from the resizing state if a resize is in progress + val width = selectionState.resizingState?.width ?: (constraints.maxWidth - remainingOffset) + val placeable = measurable.measure(constraints.copy(minWidth = width, maxWidth = width)) + layout(constraints.maxWidth, placeable.height) { placeable.place(0, 0) } + } +} + private object SelectionDefaults { val ResizingDotSize = 16.dp + val SelectedBorderWidth = 2.dp } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt index b1841c4c5ffa..c0441f8a38a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/model/TileGridCell.kt @@ -31,8 +31,8 @@ sealed interface GridCell { } /** - * Represents a [EditTileViewModel] from a grid associated with a tile format and the row it's - * positioned at + * Represents a [EditTileViewModel] from a grid associated with a tile format and the row and column + * it's positioned at */ @Immutable data class TileGridCell( @@ -41,13 +41,15 @@ data class TileGridCell( override val width: Int, override val span: GridItemSpan = GridItemSpan(width), override val s: String = "${tile.tileSpec.spec}-$row-$width", + val column: Int, ) : GridCell, SizedTile<EditTileViewModel>, CategoryAndName by tile { val key: String = "${tile.tileSpec.spec}-$row" constructor( sizedTile: SizedTile<EditTileViewModel>, row: Int, - ) : this(tile = sizedTile.tile, row = row, width = sizedTile.width) + column: Int, + ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width) } /** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */ @@ -73,7 +75,13 @@ fun List<SizedTile<EditTileViewModel>>.toGridCells( return splitInRowsSequence(this, columns) .flatMapIndexed { rowIndex, sizedTiles -> val correctedRowIndex = rowIndex + startingRow - val row: List<GridCell> = sizedTiles.map { TileGridCell(it, correctedRowIndex) } + var column = 0 + val row: List<GridCell> = + sizedTiles.map { + TileGridCell(it, correctedRowIndex, column).also { cell -> + column += cell.width + } + } // Fill the incomplete rows with spacers val numSpacers = columns - sizedTiles.sumOf { it.width } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.kt new file mode 100644 index 000000000000..506b05256880 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/BounceableTileViewModel.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 + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.VectorConverter +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.android.compose.animation.Bounceable + +class BounceableTileViewModel : Bounceable { + private val animatableBounce = Animatable(0.dp, Dp.VectorConverter) + override val bounce: Dp + get() = animatableBounce.value + + suspend fun animateBounce() { + animatableBounce.animateTo(BounceSize) + animatableBounce.animateTo(0.dp) + } + + private companion object { + val BounceSize = 8.dp + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt index ee12736f6db4..be6ce5c5b4f4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt @@ -43,13 +43,13 @@ data class UnloadedEditTileViewModel( ) { fun load(context: Context): EditTileViewModel { return EditTileViewModel( - tileSpec, - icon, - label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), - appName?.toAnnotatedString(context), - isCurrent, - availableEditActions, - category, + tileSpec = tileSpec, + icon = icon, + label = label.toAnnotatedString(context) ?: AnnotatedString(tileSpec.spec), + appName = appName?.toAnnotatedString(context), + isCurrent = isCurrent, + availableEditActions = availableEditActions, + category = category, ) } } 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 b8f4ab40bb1d..dde36289f139 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 @@ -16,7 +16,7 @@ package com.android.systemui.qs.tiles.base.viewmodel -import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -28,5 +28,7 @@ class QSTileCoroutineScopeFactory constructor(@Application private val applicationScope: CoroutineScope) { fun create(): CoroutineScope = - CoroutineScope(applicationScope.coroutineContext + SupervisorJob() + createCoroutineTracingContext("QSTileScope")) + CoroutineScope( + applicationScope.coroutineContext + SupervisorJob() + newTracingContext("QSTileScope") + ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt index ae56c2aad4e9..f6749715e1fd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt @@ -15,12 +15,12 @@ */ package com.android.systemui.qs.tiles.dialog -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.util.Log import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.animation.Expandable +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.phone.SystemUIDialog @@ -63,7 +63,7 @@ constructor( } return } else { - coroutineScope = CoroutineScope(bgDispatcher + createCoroutineTracingContext("InternetDialogScope")) + coroutineScope = CoroutineScope(bgDispatcher + newTracingContext("InternetDialogScope")) dialog = dialogFactory .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt index ac75932b8fee..14115444fe49 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt @@ -16,9 +16,9 @@ package com.android.systemui.qs.tiles.impl.location.domain.interactor -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.content.Intent import android.provider.Settings +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.plugins.ActivityStarter @@ -53,9 +53,11 @@ constructor( val wasEnabled: Boolean = input.data.isEnabled if (keyguardController.isMethodSecure() && keyguardController.isShowing()) { activityStarter.postQSRunnableDismissingKeyguard { - CoroutineScope(applicationScope.coroutineContext + createCoroutineTracingContext("LocationTileScope")).launch { - locationController.setLocationEnabled(!wasEnabled) - } + CoroutineScope( + applicationScope.coroutineContext + + newTracingContext("LocationTileScope") + ) + .launch { locationController.setLocationEnabled(!wasEnabled) } } } else { withContext(coroutineContext) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 40591bf56e0a..cc14e71986f5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor import android.content.Context import android.os.UserHandle -import com.android.app.tracing.coroutines.flow.map +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.asIcon import com.android.systemui.dagger.qualifiers.Background @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map class ModesTileDataInteractor @Inject @@ -59,6 +60,7 @@ constructor( fun tileData() = zenModeInteractor.activeModes .map { activeModes -> buildTileData(activeModes) } + .flowName("tileData") .flowOn(bgDispatcher) .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt index 468e180a6e41..f03c7521931c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverDialogDelegate.kt @@ -16,12 +16,12 @@ package com.android.systemui.qs.tiles.impl.saver.domain -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.content.Context import android.content.DialogInterface import android.content.SharedPreferences import android.os.Bundle import com.android.internal.R +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.DataSaverController @@ -45,9 +45,8 @@ class DataSaverDialogDelegate( setTitle(R.string.data_saver_enable_title) setMessage(R.string.data_saver_description) setPositiveButton(R.string.data_saver_enable_button) { _: DialogInterface?, _ -> - CoroutineScope(backgroundContext + createCoroutineTracingContext("DataSaverDialogScope")).launch { - dataSaverController.setDataSaverEnabled(true) - } + CoroutineScope(backgroundContext + newTracingContext("DataSaverDialogScope")) + .launch { dataSaverController.setDataSaverEnabled(true) } sharedPreferences .edit() diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt index ce942fefa393..47254775618c 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt @@ -19,7 +19,7 @@ package com.android.systemui.scene.ui.viewmodel import android.view.MotionEvent import android.view.View import androidx.compose.runtime.getValue -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.DefaultEdgeDetector import com.android.compose.animation.scene.ObservableTransitionState diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 700253babb82..c3de06778ee5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -61,6 +61,9 @@ public class RecordingService extends Service implements ScreenMediaRecorderList private static final int USER_ID_NOT_SPECIFIED = -1; protected static final int NOTIF_BASE_ID = 4273; + protected static final int NOTIF_GROUP_ID_SAVED = NOTIF_BASE_ID + 1; + protected static final int NOTIF_GROUP_ID_ERROR_SAVING = NOTIF_BASE_ID + 2; + protected static final int NOTIF_GROUP_ID_ERROR_STARTING = NOTIF_BASE_ID + 3; private static final String TAG = "RecordingService"; private static final String CHANNEL_ID = "screen_record"; @VisibleForTesting static final String GROUP_KEY_SAVED = "screen_record_saved"; @@ -280,7 +283,11 @@ public class RecordingService extends Service implements ScreenMediaRecorderList */ @VisibleForTesting protected void createErrorStartingNotification(UserHandle currentUser) { - createErrorNotification(currentUser, strings().getStartError(), GROUP_KEY_ERROR_STARTING); + createErrorNotification( + currentUser, + strings().getStartError(), + GROUP_KEY_ERROR_STARTING, + NOTIF_GROUP_ID_ERROR_STARTING); } /** @@ -289,13 +296,21 @@ public class RecordingService extends Service implements ScreenMediaRecorderList */ @VisibleForTesting protected void createErrorSavingNotification(UserHandle currentUser) { - createErrorNotification(currentUser, strings().getSaveError(), GROUP_KEY_ERROR_SAVING); + createErrorNotification( + currentUser, + strings().getSaveError(), + GROUP_KEY_ERROR_SAVING, + NOTIF_GROUP_ID_ERROR_SAVING); } private void createErrorNotification( - UserHandle currentUser, String notificationContentTitle, String groupKey) { + UserHandle currentUser, + String notificationContentTitle, + String groupKey, + int notificationIdForGroup) { // Make sure error notifications get their own group. - postGroupSummaryNotification(currentUser, notificationContentTitle, groupKey); + postGroupSummaryNotification( + currentUser, notificationContentTitle, groupKey, notificationIdForGroup); Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, strings().getTitle()); @@ -410,17 +425,20 @@ public class RecordingService extends Service implements ScreenMediaRecorderList } /** - * Adds a group summary notification for save notifications so that save notifications from - * multiple recordings are grouped together, and the foreground service recording notification - * is not. + * Posts a group summary notification for the given group. + * + * Notifications that should be grouped: + * - Save notifications + * - Error saving notifications + * - Error starting notifications + * + * The foreground service recording notification should never be grouped. */ - private void postGroupSummaryNotificationForSaves(UserHandle currentUser) { - postGroupSummaryNotification(currentUser, strings().getSaveTitle(), GROUP_KEY_SAVED); - } - - /** Posts a group summary notification for the given group. */ private void postGroupSummaryNotification( - UserHandle currentUser, String notificationContentTitle, String groupKey) { + UserHandle currentUser, + String notificationContentTitle, + String groupKey, + int notificationIdForGroup) { Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, strings().getTitle()); @@ -431,7 +449,8 @@ public class RecordingService extends Service implements ScreenMediaRecorderList .setGroupSummary(true) .setExtras(extras) .build(); - mNotificationManager.notifyAsUser(getTag(), mNotificationId, groupNotif, currentUser); + mNotificationManager.notifyAsUser( + getTag(), notificationIdForGroup, groupNotif, currentUser); } private void stopService() { @@ -484,7 +503,11 @@ public class RecordingService extends Service implements ScreenMediaRecorderList Log.d(getTag(), "saving recording"); Notification notification = createSaveNotification( getRecorder() != null ? getRecorder().save() : null); - postGroupSummaryNotificationForSaves(currentUser); + postGroupSummaryNotification( + currentUser, + strings().getSaveTitle(), + GROUP_KEY_SAVED, + NOTIF_GROUP_ID_SAVED); mNotificationManager.notifyAsUser(null, mNotificationId, notification, currentUser); } catch (IOException | IllegalStateException e) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt index 54e0319da58f..17b1b6d91229 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt @@ -26,7 +26,7 @@ import android.os.UserHandle import android.util.Log import android.util.Pair import android.view.Window -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.app.ChooserActivity import com.android.systemui.dagger.qualifiers.Application import dagger.assisted.Assisted diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt index 9e622801a01b..7b01c36489fb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt @@ -31,7 +31,7 @@ import android.view.RemoteAnimationAdapter import android.view.RemoteAnimationTarget import android.view.WindowManager import android.view.WindowManagerGlobal -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.infra.ServiceConnector import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt index c8067df114fc..6df22f036273 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt @@ -21,7 +21,7 @@ import android.os.RemoteException import android.util.Log import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter import com.android.systemui.shade.ShadeExpansionStateManager diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt index d3a7fc4a3e4a..7aeec47241cb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSoundController.kt @@ -18,7 +18,7 @@ package com.android.systemui.screenshot import android.media.MediaPlayer import android.util.Log -import com.android.app.tracing.coroutines.async +import com.android.app.tracing.coroutines.asyncTraced as async import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 757e37e9341f..d652d5bec27b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -123,7 +123,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.doze.DozeLog; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; @@ -1859,16 +1858,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump /** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */ private float getLockIconPadding() { float lockIconPadding = 0f; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView() - .findViewById(R.id.device_entry_icon_view); - if (deviceEntryIconView != null) { - lockIconPadding = mNotificationStackScrollLayoutController.getBottom() - - deviceEntryIconView.getTop(); - } - } else if (mLockIconViewController.getTop() != 0f) { + View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView() + .findViewById(R.id.device_entry_icon_view); + if (deviceEntryIconView != null) { lockIconPadding = mNotificationStackScrollLayoutController.getBottom() - - mLockIconViewController.getTop(); + - deviceEntryIconView.getTop(); } return lockIconPadding; } @@ -5059,8 +5053,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return false; } - if (DeviceEntryUdfpsRefactor.isEnabled() - && mAlternateBouncerInteractor.isVisibleState()) { + if (mAlternateBouncerInteractor.isVisibleState()) { // never send touches to shade if the alternate bouncer is showing return false; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index cae141d014a8..5699557c14e7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -35,7 +35,6 @@ import androidx.core.view.ViewKt; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardUnfoldTransition; -import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -44,7 +43,6 @@ import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags; import com.android.systemui.bouncer.ui.binder.BouncerViewBinder; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlagsClassic; @@ -75,7 +73,6 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeScrimController; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; @@ -101,9 +98,7 @@ public class NotificationShadeWindowViewController implements Dumpable { private final NotificationShadeDepthController mDepthController; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; - private final LockIconViewController mLockIconViewController; private final ShadeLogger mShadeLogger; - private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final StatusBarWindowStateController mStatusBarWindowStateController; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final AmbientState mAmbientState; @@ -174,9 +169,7 @@ public class NotificationShadeWindowViewController implements Dumpable { PanelExpansionInteractor panelExpansionInteractor, ShadeExpansionStateManager shadeExpansionStateManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, - StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowStateController statusBarWindowStateController, - LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, DozeServiceHost dozeServiceHost, DozeScrimController dozeScrimController, @@ -210,9 +203,7 @@ public class NotificationShadeWindowViewController implements Dumpable { mShadeExpansionStateManager = shadeExpansionStateManager; mDepthController = depthController; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; - mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarWindowStateController = statusBarWindowStateController; - mLockIconViewController = lockIconViewController; mShadeLogger = shadeLogger; mService = centralSurfaces; mDozeServiceHost = dozeServiceHost; @@ -259,7 +250,6 @@ public class NotificationShadeWindowViewController implements Dumpable { mDisableSubpixelTextTransitionListener)); } - lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view)); dumpManager.registerDumpable(this); } @@ -392,9 +382,6 @@ public class NotificationShadeWindowViewController implements Dumpable { } if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { - // If the user was sliding their finger across the lock screen, - // we may have been intercepting the touch and forwarding it to the - // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below). // If this touch ended up unlocking the device, we want to cancel the touch // immediately, so we don't cause swipe or expand animations afterwards. cancelCurrentTouch(); @@ -419,9 +406,6 @@ public class NotificationShadeWindowViewController implements Dumpable { && mDreamingWakeupGestureHandler.onTouchEvent(ev)) { return logDownDispatch(ev, "dream wakeup gesture handled", true); } - if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) { - return logDownDispatch(ev, "dispatched to Keyguard", true); - } if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == View.VISIBLE) { // Disallow new pointers while the brightness mirror is visible. This is so that @@ -512,7 +496,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing() && !mDockManager.isDocked() - && !mLockIconViewController.willHandleTouchWhileDozing(ev) ) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { mShadeLogger.d("NSWVC: capture all touch events in always-on"); @@ -520,22 +503,8 @@ public class NotificationShadeWindowViewController implements Dumpable { return true; } - if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) { - // Don't allow touches to proceed to underlying views if alternate - // bouncer is showing - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mShadeLogger.d("NSWVC: alt bouncer showing"); - } - return true; - } - - boolean bouncerShowing; - if (DeviceEntryUdfpsRefactor.isEnabled()) { - bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() + boolean bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() || mAlternateBouncerInteractor.isVisibleState(); - } else { - bouncerShowing = mService.isBouncerShowing(); - } if (mPanelExpansionInteractor.isFullyExpanded() && !bouncerShowing && !mStatusBarStateController.isDozing()) { @@ -603,9 +572,6 @@ public class NotificationShadeWindowViewController implements Dumpable { if (mStatusBarStateController.isDozing()) { handled = !mDozeServiceHost.isPulsing(); } - if (mStatusBarKeyguardViewManager.onTouch(ev)) { - return true; - } if (MigrateClocksToBlueprint.isEnabled()) { if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) { // we still want to finish our drag down gesture when locking the screen diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index b7a95e989317..0e30f2b4bb30 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import dagger.Lazy; @@ -62,7 +62,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl { private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarStateController mStatusBarStateController; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private final StatusBarWindowController mStatusBarWindowController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; private final DeviceProvisionedController mDeviceProvisionedController; private final Lazy<NotificationShadeWindowViewController> mNotifShadeWindowViewController; @@ -83,7 +83,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl { KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, - StatusBarWindowController statusBarWindowController, + StatusBarWindowControllerStore statusBarWindowControllerStore, DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, @DisplayId int displayId, @@ -102,7 +102,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl { mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor; mNpvc = shadeViewControllerLazy; mStatusBarStateController = statusBarStateController; - mStatusBarWindowController = statusBarWindowController; + mStatusBarWindowControllerStore = statusBarWindowControllerStore; mDeviceProvisionedController = deviceProvisionedController; mGutsManager = gutsManager; mNotificationShadeWindowController = notificationShadeWindowController; @@ -315,7 +315,7 @@ public final class ShadeControllerImpl extends BaseShadeControllerImpl { // Update the visibility of notification shade and status bar window. mNotificationShadeWindowController.setPanelVisible(false); - mStatusBarWindowController.setForceStatusBarVisible(false); + mStatusBarWindowControllerStore.getDefaultDisplay().setForceStatusBarVisible(false); // Close any guts that might be visible mGutsManager.get().closeAndSaveGuts( diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt index 949d2aa36bf3..460bfbbcb3ab 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImpl.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade.domain.interactor +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardRepository @@ -62,17 +63,20 @@ constructor( override val isShadeEnabled: StateFlow<Boolean> = disableFlagsRepository.disableFlags .map { it.isShadeEnabled() } + .flowName("isShadeEnabled") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isQsEnabled: StateFlow<Boolean> = disableFlagsRepository.disableFlags .map { it.isQuickSettingsEnabled() } + .flowName("isQsEnabled") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isAnyFullyExpanded: StateFlow<Boolean> = anyExpansion .map { it >= 1f } .distinctUntilChanged() + .flowName("isAnyFullyExpanded") .stateIn(scope, SharingStarted.Eagerly, initialValue = false) override val isShadeFullyExpanded: Flow<Boolean> = @@ -81,6 +85,7 @@ constructor( override val isShadeAnyExpanded: StateFlow<Boolean> = baseShadeInteractor.shadeExpansion .map { it > 0 } + .flowName("isShadeAnyExpanded") .stateIn(scope, SharingStarted.Eagerly, false) override val isShadeFullyCollapsed: Flow<Boolean> = @@ -88,6 +93,7 @@ constructor( override val isUserInteracting: StateFlow<Boolean> = combine(isUserInteractingWithShade, isUserInteractingWithQs) { shade, qs -> shade || qs } + .flowName("isUserInteracting") .stateIn(scope, SharingStarted.Eagerly, false) override val isShadeTouchable: Flow<Boolean> = diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt index 3113dc462e6a..4bdd36773655 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActionsViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.shade.ui.viewmodel -import com.android.app.tracing.coroutines.flow.map import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.UserAction @@ -33,6 +32,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map /** * Models the UI state for the user actions that the user can perform to navigate to other scenes. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 1481b734ff61..1ec5357d0d30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -15,7 +15,6 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.ExpandHelper import com.android.systemui.Gefingerpoken -import com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy import com.android.systemui.classifier.Classifier import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton @@ -90,6 +89,7 @@ constructor( @get:VisibleForTesting var fractionToShade: Float = 0f private set + private var useSplitShade: Boolean = false private lateinit var nsslController: NotificationStackScrollLayoutController lateinit var centralSurfaces: CentralSurfaces @@ -161,9 +161,6 @@ constructor( val distanceUntilShowingPulsingNotifications get() = fullTransitionDistance - /** The udfpsKeyguardViewController if it exists. */ - var mUdfpsKeyguardViewControllerLegacy: UdfpsKeyguardViewControllerLegacy? = null - /** The touch helper responsible for the drag down animation. */ val touchHelper = DragDownHelper( @@ -171,7 +168,7 @@ constructor( this, naturalScrollingSettingObserver, shadeRepository, - context + context, ) private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy { @@ -448,7 +445,6 @@ constructor( val udfpsProgress = MathUtils.saturate(dragDownAmount / udfpsTransitionDistance) shadeRepository.setUdfpsTransitionToFullShadeProgress(udfpsProgress) - mUdfpsKeyguardViewControllerLegacy?.setTransitionToFullShadeProgress(udfpsProgress) val statusBarProgress = MathUtils.saturate(dragDownAmount / statusBarTransitionDistance) centralSurfaces.setTransitionToFullShadeProgress(statusBarProgress) @@ -457,7 +453,7 @@ constructor( private fun setDragDownAmountAnimated( target: Float, delay: Long = 0, - endlistener: (() -> Unit)? = null + endlistener: (() -> Unit)? = null, ) { logger.logDragDownAnimation(target) val dragDownAnimator = ValueAnimator.ofFloat(dragDownAmount, target) @@ -553,7 +549,7 @@ constructor( private fun goToLockedShadeInternal( expandView: View?, animationHandler: ((Long) -> Unit)? = null, - cancelAction: Runnable? = null + cancelAction: Runnable? = null, ) { if (!shadeInteractor.isShadeEnabled.value) { cancelAction?.run() @@ -564,10 +560,7 @@ constructor( var entry: NotificationEntry? = null if (expandView is ExpandableNotificationRow) { entry = expandView.entry - entry.setUserExpanded( - /* userExpanded= */ true, - /* allowChildExpansion= */ true, - ) + entry.setUserExpanded(/* userExpanded= */ true, /* allowChildExpansion= */ true) // Indicate that the group expansion is changing at this time -- this way the group // and children backgrounds / divider animations will look correct. entry.setGroupExpansionChanging(true) @@ -594,9 +587,7 @@ constructor( statusBarStateController.setLeaveOpenOnKeyguardHide(false) draggedDownEntry?.apply { setUserLocked(false) - notifyHeightChanged( - /* needsAnimation= */ false, - ) + notifyHeightChanged(/* needsAnimation= */ false) draggedDownEntry = null } cancelAction?.run() @@ -614,9 +605,7 @@ constructor( // This call needs to be after updating the shade state since otherwise // the scrimstate resets too early if (animationHandler != null) { - animationHandler.invoke( - /* delay= */ 0, - ) + animationHandler.invoke(/* delay= */ 0) } else { performDefaultGoToFullShadeAnimation(0) } @@ -757,7 +746,7 @@ class DragDownHelper( private val dragDownCallback: LockscreenShadeTransitionController, private val naturalScrollingSettingObserver: NaturalScrollingSettingObserver, private val shadeRepository: ShadeRepository, - context: Context + context: Context, ) : Gefingerpoken { private var dragDownAmountOnStart = 0.0f @@ -932,7 +921,7 @@ class DragDownHelper( @VisibleForTesting fun cancelChildExpansion( child: ExpandableView, - animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS + animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS, ) { if (child.actualHeight == child.collapsedHeight) { expandCallback.setUserLockedChild(child, false) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt index be733d4e1e8e..8ce0dbf8e171 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt @@ -20,7 +20,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel import dagger.Binds import dagger.Module import dagger.Provides @@ -31,8 +31,8 @@ import dagger.multibindings.IntoMap abstract class StatusBarChipsModule { @Binds @IntoMap - @ClassKey(DemoRonChipViewModel::class) - abstract fun binds(impl: DemoRonChipViewModel): CoreStartable + @ClassKey(DemoNotifChipViewModel::class) + abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable companion object { @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt index cce9a1624d51..5fa19ddef1be 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.pm.PackageManager import android.content.pm.PackageManager.NameNotFoundException @@ -22,7 +22,7 @@ import android.graphics.drawable.Drawable import com.android.systemui.CoreStartable import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +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.chips.ui.viewmodel.OngoingActivityChipViewModel @@ -37,25 +37,25 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow /** - * A view model that will emit demo RON chips (rich ongoing notification chips) from [chip] based on - * adb commands sent by the user. + * A view model that will emit demo promoted ongoing notification chips from [chip] based on adb + * commands sent by the user. * * Example adb commands: * * To show a chip with the SysUI icon and custom text and color: * ``` - * adb shell cmd statusbar demo-ron -p com.android.systemui -t 10min -c "\\#434343" + * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343" * ``` * * To hide the chip: * ``` - * adb shell cmd statusbar demo-ron --hide + * adb shell cmd statusbar demo-notif --hide * ``` * - * See [DemoRonCommand] for more information on the adb command spec. + * See [DemoNotifCommand] for more information on the adb command spec. */ @SysUISingleton -class DemoRonChipViewModel +class DemoNotifChipViewModel @Inject constructor( private val commandRegistry: CommandRegistry, @@ -63,19 +63,19 @@ constructor( private val systemClock: SystemClock, ) : OngoingActivityChipViewModel, CoreStartable { override fun start() { - commandRegistry.registerCommand("demo-ron") { DemoRonCommand() } + commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() } } private val _chip = MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden()) override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow() - private inner class DemoRonCommand : ParseableCommand("demo-ron") { + private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) { private val packageName: String? by param( longName = "packageName", shortName = "p", - description = "The package name for the demo RON app", + description = "The package name for app \"posting\" the demo notification", valueParser = Type.String, ) @@ -99,15 +99,12 @@ constructor( ) private val hide by - flag( - longName = "hide", - description = "Hides any existing demo RON chip", - ) + flag(longName = "hide", description = "Hides any existing demo notification chip") override fun execute(pw: PrintWriter) { - if (!StatusBarRonChips.isEnabled) { + if (!StatusBarNotifChips.isEnabled) { pw.println( - "Error: com.android.systemui.status_bar_ron_chips must be enabled " + + "Error: com.android.systemui.status_bar_notification_chips must be enabled " + "before using this demo feature" ) return @@ -167,8 +164,12 @@ constructor( return null } return OngoingActivityChipModel.ChipIcon.FullColorAppIcon( - Icon.Loaded(drawable = iconDrawable, contentDescription = null), + Icon.Loaded(drawable = iconDrawable, contentDescription = null) ) } } + + companion object { + private const val DEMO_COMMAND_NAME = "demo-notif" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt index 4ef190991f19..47ffbafe3735 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ron/shared/StatusBarRonChips.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/shared/StatusBarNotifChips.kt @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.shared +package com.android.systemui.statusbar.chips.notification.shared import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils -/** Helper for reading or using the status bar RON chips flag state. */ +/** Helper for reading or using the status bar promoted notification chips flag state. */ @Suppress("NOTHING_TO_INLINE") -object StatusBarRonChips { +object StatusBarNotifChips { /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_STATUS_BAR_RON_CHIPS + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_NOTIFICATION_CHIPS /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object StatusBarRonChips { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.statusBarRonChips() + get() = Flags.statusBarNotificationChips() /** * Called to ensure code is only run when the flag is enabled. This protects users from the 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 new file mode 100644 index 000000000000..6ae92637bde7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.chips.ui.model.ColorsModel +import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor +import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** A view model for status bar chips for promoted ongoing notifications. */ +@SysUISingleton +class NotifChipsViewModel +@Inject +constructor(activeNotificationsInteractor: ActiveNotificationsInteractor) { + /** + * A flow modeling the notification chips that should be shown. Emits an empty list if there are + * no notifications that should show a status bar chip. + */ + val chips: Flow<List<OngoingActivityChipModel.Shown>> = + activeNotificationsInteractor.promotedOngoingNotifications.map { notifications -> + notifications.mapNotNull { it.toChipModel() } + } + + /** + * Converts the notification to the [OngoingActivityChipModel] object. Returns null if the + * notification has invalid data such that it can't be displayed as a chip. + */ + private fun ActiveNotificationModel.toChipModel(): OngoingActivityChipModel.Shown? { + // TODO(b/364653005): Log error if there's no icon view. + val rawIcon = this.statusBarChipIconView ?: return null + val icon = OngoingActivityChipModel.ChipIcon.StatusBarView(rawIcon) + // TODO(b/364653005): Use the notification color if applicable. + val colors = ColorsModel.Themed + // TODO(b/364653005): When the chip is clicked, show the HUN. + val onClickListener = null + return OngoingActivityChipModel.Shown.ShortTimeDelta( + icon, + colors, + time = this.whenTime, + onClickListener, + ) + // TODO(b/364653005): If Notification.showWhen = false, don't show the time delta. + // TODO(b/364653005): If Notification.whenTime is in the past, show "ago" in the text. + // TODO(b/364653005): If Notification.shortCriticalText is set, use that instead of `when`. + // TODO(b/364653005): If the app that posted the notification is in the foreground, don't + // show that app's chip. + } +} 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 3b1e565e122b..f4462a434477 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 @@ -21,13 +21,14 @@ import android.content.res.ColorStateList import android.graphics.drawable.GradientDrawable import android.view.View import android.view.ViewGroup +import android.widget.DateTimeView import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips 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 @@ -42,6 +43,8 @@ object OngoingActivityChipBinder { val chipTimeView: ChipChronometer = chipView.requireViewById(R.id.ongoing_activity_chip_time) val chipTextView: TextView = chipView.requireViewById(R.id.ongoing_activity_chip_text) + val chipShortTimeDeltaView: DateTimeView = + chipView.requireViewById(R.id.ongoing_activity_chip_short_time_delta) val chipBackgroundView: ChipBackgroundContainer = chipView.requireViewById(R.id.ongoing_activity_chip_background) @@ -49,13 +52,14 @@ object OngoingActivityChipBinder { is OngoingActivityChipModel.Shown -> { // Data setChipIcon(chipModel, chipBackgroundView, chipDefaultIconView) - setChipMainContent(chipModel, chipTextView, chipTimeView) + setChipMainContent(chipModel, chipTextView, chipTimeView, chipShortTimeDeltaView) chipView.setOnClickListener(chipModel.onClickListener) updateChipPadding( chipModel, chipBackgroundView, chipTextView, chipTimeView, + chipShortTimeDeltaView, ) // Accessibility @@ -65,6 +69,7 @@ object OngoingActivityChipBinder { val textColor = chipModel.colors.text(chipContext) chipTimeView.setTextColor(textColor) chipTextView.setTextColor(textColor) + chipShortTimeDeltaView.setTextColor(textColor) (chipBackgroundView.background as GradientDrawable).color = chipModel.colors.background(chipContext) } @@ -97,7 +102,7 @@ object OngoingActivityChipBinder { defaultIconView.tintView(iconTint) } is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> { - StatusBarRonChips.assertInNewMode() + StatusBarNotifChips.assertInNewMode() IconViewBinder.bind(icon.impl, defaultIconView) defaultIconView.visibility = View.VISIBLE defaultIconView.untintView() @@ -161,6 +166,7 @@ object OngoingActivityChipBinder { chipModel: OngoingActivityChipModel.Shown, chipTextView: TextView, chipTimeView: ChipChronometer, + chipShortTimeDeltaView: DateTimeView, ) { when (chipModel) { is OngoingActivityChipModel.Shown.Countdown -> { @@ -168,21 +174,35 @@ object OngoingActivityChipBinder { chipTextView.visibility = View.VISIBLE chipTimeView.hide() + chipShortTimeDeltaView.visibility = View.GONE } is OngoingActivityChipModel.Shown.Text -> { chipTextView.text = chipModel.text chipTextView.visibility = View.VISIBLE chipTimeView.hide() + chipShortTimeDeltaView.visibility = View.GONE } is OngoingActivityChipModel.Shown.Timer -> { ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) chipTimeView.visibility = View.VISIBLE chipTextView.visibility = View.GONE + chipShortTimeDeltaView.visibility = View.GONE + } + is OngoingActivityChipModel.Shown.ShortTimeDelta -> { + chipShortTimeDeltaView.setTime(chipModel.time) + // TODO(b/364653005): DateTimeView's relative time doesn't quite match the format we + // want in the status bar chips. + chipShortTimeDeltaView.isShowRelativeTime = true + chipShortTimeDeltaView.visibility = View.VISIBLE + + chipTextView.visibility = View.GONE + chipTimeView.hide() } is OngoingActivityChipModel.Shown.IconOnly -> { chipTextView.visibility = View.GONE + chipShortTimeDeltaView.visibility = View.GONE chipTimeView.hide() } } @@ -200,6 +220,7 @@ object OngoingActivityChipBinder { backgroundView: View, chipTextView: TextView, chipTimeView: ChipChronometer, + chipShortTimeDeltaView: DateTimeView, ) { if (chipModel.icon != null) { if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) { @@ -209,15 +230,18 @@ object OngoingActivityChipBinder { backgroundView.setBackgroundPaddingForEmbeddedPaddingIcon() chipTextView.setTextPaddingForEmbeddedPaddingIcon() chipTimeView.setTextPaddingForEmbeddedPaddingIcon() + chipShortTimeDeltaView.setTextPaddingForEmbeddedPaddingIcon() } else { backgroundView.setBackgroundPaddingForNormalIcon() chipTextView.setTextPaddingForNormalIcon() chipTimeView.setTextPaddingForNormalIcon() + chipShortTimeDeltaView.setTextPaddingForNormalIcon() } } else { backgroundView.setBackgroundPaddingForNoIcon() chipTextView.setTextPaddingForNoIcon() chipTimeView.setTextPaddingForNoIcon() + chipShortTimeDeltaView.setTextPaddingForNoIcon() } } @@ -258,23 +282,13 @@ object OngoingActivityChipBinder { context.resources.getDimensionPixelSize( R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon ) - setPaddingRelative( - sidePadding, - paddingTop, - sidePadding, - paddingBottom, - ) + setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom) } private fun View.setBackgroundPaddingForNormalIcon() { val sidePadding = context.resources.getDimensionPixelSize(R.dimen.ongoing_activity_chip_side_padding) - setPaddingRelative( - sidePadding, - paddingTop, - sidePadding, - paddingBottom, - ) + setPaddingRelative(sidePadding, paddingTop, sidePadding, paddingBottom) } private fun View.setBackgroundPaddingForNoIcon() { 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 62622a893ec5..cf07af1fc5dd 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 @@ -20,6 +20,7 @@ import android.view.View import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips /** Model representing the display of an ongoing activity as a chip in the status bar. */ sealed class OngoingActivityChipModel { @@ -79,6 +80,24 @@ sealed class OngoingActivityChipModel { } /** + * The chip shows the time delta between now and [time] in a short format, e.g. "15min" or + * "1hr ago". + */ + data class ShortTimeDelta( + override val icon: ChipIcon, + override val colors: ColorsModel, + /** The time of the event that this chip represents. */ + val time: Long, + override val onClickListener: View.OnClickListener?, + ) : Shown(icon, colors, onClickListener) { + init { + StatusBarNotifChips.assertInNewMode() + } + + override val logName = "Shown.ShortTimeDelta" + } + + /** * This chip shows a countdown using [secondsUntilStarted]. Used to inform users that an * event is about to start. Typically, a [Countdown] chip will turn into a [Timer] chip. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index 24c1b879f429..ed325970ebb2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.chips.ui.viewmodel -import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer @@ -24,19 +23,20 @@ import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.chips.StatusBarChipsLog import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.DemoRonChipViewModel -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.util.kotlin.combine import com.android.systemui.util.kotlin.pairwise import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -55,7 +55,8 @@ constructor( shareToAppChipViewModel: ShareToAppChipViewModel, castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel, callChipViewModel: CallChipViewModel, - demoRonChipViewModel: DemoRonChipViewModel, + notifChipsViewModel: NotifChipsViewModel, + demoNotifChipViewModel: DemoNotifChipViewModel, @StatusBarChipsLog private val logger: LogBuffer, ) { private enum class ChipType { @@ -63,8 +64,9 @@ constructor( ShareToApp, CastToOtherDevice, Call, - /** A demo of a RON chip (rich ongoing notification chip), used just for testing. */ - DemoRon, + Notification, + /** A demo of a notification chip, used just for testing. */ + DemoNotification, } /** Model that helps us internally track the various chip states from each of the types. */ @@ -85,7 +87,8 @@ constructor( val shareToApp: OngoingActivityChipModel.Hidden, val castToOtherDevice: OngoingActivityChipModel.Hidden, val call: OngoingActivityChipModel.Hidden, - val demoRon: OngoingActivityChipModel.Hidden, + val notifs: OngoingActivityChipModel.Hidden, + val demoNotif: OngoingActivityChipModel.Hidden, ) : InternalChipModel } @@ -94,7 +97,8 @@ constructor( val shareToApp: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), - val demoRon: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), + val notifs: List<OngoingActivityChipModel.Shown> = emptyList(), + val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(), ) /** Bundles all the incoming chips into one object to easily pass to various flows. */ @@ -104,8 +108,9 @@ constructor( shareToAppChipViewModel.chip, castToOtherDeviceChipViewModel.chip, callChipViewModel.chip, - demoRonChipViewModel.chip, - ) { screenRecord, shareToApp, castToOtherDevice, call, demoRon -> + notifChipsViewModel.chips, + demoNotifChipViewModel.chip, + ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif -> logger.log( TAG, LogLevel.INFO, @@ -121,16 +126,19 @@ constructor( LogLevel.INFO, { str1 = call.logName - str2 = demoRon.logName + // TODO(b/364653005): Log other information for notification chips. + str2 = notifs.map { it.logName }.toString() + str3 = demoNotif.logName }, - { "... > Call=$str1 > DemoRon=$str2" } + { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" }, ) ChipBundle( screenRecord = screenRecord, shareToApp = shareToApp, castToOtherDevice = castToOtherDevice, call = call, - demoRon = demoRon, + notifs = notifs, + demoNotif = demoNotif, ) } // Some of the chips could have timers in them and we don't want the start time @@ -189,9 +197,9 @@ constructor( * actually displaying the chip. */ val chips: StateFlow<MultipleOngoingActivityChipsModel> = - if (!Flags.statusBarRonChips()) { - // Multiple chips are only allowed with RONs. If the flag isn't on, use just the - // primary chip. + if (!StatusBarNotifChips.isEnabled) { + // Multiple chips are only allowed with notification chips. If the flag isn't on, use + // just the primary chip. primaryChip .map { MultipleOngoingActivityChipsModel( @@ -199,11 +207,7 @@ constructor( secondary = OngoingActivityChipModel.Hidden(), ) } - .stateIn( - scope, - SharingStarted.Lazily, - MultipleOngoingActivityChipsModel(), - ) + .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel()) } else { internalChips .pairwise(initialValue = DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL) @@ -212,11 +216,7 @@ constructor( val correctSecondary = createOutputModel(old.secondary, new.secondary) MultipleOngoingActivityChipsModel(correctPrimary, correctSecondary) } - .stateIn( - scope, - SharingStarted.Lazily, - MultipleOngoingActivityChipsModel(), - ) + .stateIn(scope, SharingStarted.Lazily, MultipleOngoingActivityChipsModel()) } /** A data class representing the return result of [pickMostImportantChip]. */ @@ -251,7 +251,7 @@ constructor( // suppress the share-to-app chip to make sure they don't both show. // See b/296461748. shareToApp = OngoingActivityChipModel.Hidden(), - ) + ), ) bundle.shareToApp is OngoingActivityChipModel.Shown -> MostImportantChipResult( @@ -274,11 +274,19 @@ constructor( mostImportantChip = InternalChipModel.Shown(ChipType.Call, bundle.call), remainingChips = bundle.copy(call = OngoingActivityChipModel.Hidden()), ) - bundle.demoRon is OngoingActivityChipModel.Shown -> { - StatusBarRonChips.assertInNewMode() + bundle.notifs.isNotEmpty() -> + MostImportantChipResult( + mostImportantChip = + InternalChipModel.Shown(ChipType.Notification, bundle.notifs.first()), + remainingChips = + bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)), + ) + bundle.demoNotif is OngoingActivityChipModel.Shown -> { + StatusBarNotifChips.assertInNewMode() MostImportantChipResult( - mostImportantChip = InternalChipModel.Shown(ChipType.DemoRon, bundle.demoRon), - remainingChips = bundle.copy(demoRon = OngoingActivityChipModel.Hidden()), + mostImportantChip = + InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif), + remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()), ) } else -> { @@ -287,7 +295,8 @@ constructor( check(bundle.shareToApp is OngoingActivityChipModel.Hidden) check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden) check(bundle.call is OngoingActivityChipModel.Hidden) - check(bundle.demoRon is OngoingActivityChipModel.Hidden) + check(bundle.notifs.isEmpty()) + check(bundle.demoNotif is OngoingActivityChipModel.Hidden) MostImportantChipResult( mostImportantChip = InternalChipModel.Hidden( @@ -295,7 +304,8 @@ constructor( shareToApp = bundle.shareToApp, castToOtherDevice = bundle.castToOtherDevice, call = bundle.call, - demoRon = bundle.demoRon, + notifs = OngoingActivityChipModel.Hidden(), + demoNotif = bundle.demoNotif, ), // All the chips are already hidden, so no need to filter anything out of the // bundle. @@ -323,7 +333,8 @@ constructor( ChipType.ShareToApp -> new.shareToApp ChipType.CastToOtherDevice -> new.castToOtherDevice ChipType.Call -> new.call - ChipType.DemoRon -> new.demoRon + ChipType.Notification -> new.notifs + ChipType.DemoNotification -> new.demoNotif } } else if (new is InternalChipModel.Shown) { // If we have a chip to show, always show it. @@ -344,7 +355,8 @@ constructor( shareToApp = OngoingActivityChipModel.Hidden(), castToOtherDevice = OngoingActivityChipModel.Hidden(), call = OngoingActivityChipModel.Hidden(), - demoRon = OngoingActivityChipModel.Hidden(), + notifs = OngoingActivityChipModel.Hidden(), + demoNotif = OngoingActivityChipModel.Hidden(), ) private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index 3ad76b719470..7eff8124368b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.core import android.app.Fragment import androidx.annotation.VisibleForTesting import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.fragments.FragmentHostManager import com.android.systemui.res.R import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener @@ -27,9 +26,11 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import java.lang.IllegalStateException -import javax.inject.Inject import javax.inject.Provider /** @@ -65,13 +66,17 @@ interface StatusBarInitializer { statusBarTransitions: PhoneStatusBarTransitions, ) } + + interface Factory { + fun create(displayId: Int): StatusBarInitializer + } } -@SysUISingleton class StatusBarInitializerImpl -@Inject +@AssistedInject constructor( - private val windowController: StatusBarWindowController, + @Assisted private val displayId: Int, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>, private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>, ) : CoreStartable, StatusBarInitializer { @@ -106,7 +111,7 @@ constructor( private fun doStart() { initialized = true - windowController.fragmentHostManager + statusBarWindowControllerStore.defaultDisplay.fragmentHostManager .addTagListener( CollapsedStatusBarFragment.TAG, object : FragmentHostManager.FragmentListener { @@ -137,4 +142,9 @@ constructor( ) .commit() } + + @AssistedFactory + interface Factory : StatusBarInitializer.Factory { + override fun create(displayId: Int): StatusBarInitializerImpl + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt new file mode 100644 index 000000000000..8d044bb9ce87 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.view.Display +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.display.data.repository.DisplayRepository +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** Provides per display instances of [StatusBarInitializer]. */ +interface StatusBarInitializerStore { + /** + * The instance for the default/main display of the device. For example, on a phone or a tablet, + * the default display is the internal/built-in display of the device. + * + * Note that the id of the default display is [Display.DEFAULT_DISPLAY]. + */ + val defaultDisplay: StatusBarInitializer + + /** + * Returns an instance for a specific display id. + * + * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing + * displays. + */ + fun forDisplay(displayId: Int): StatusBarInitializer +} + +@SysUISingleton +class MultiDisplayStatusBarInitializerStore +@Inject +constructor( + @Background private val backgroundApplicationScope: CoroutineScope, + private val factory: StatusBarInitializer.Factory, + private val displayRepository: DisplayRepository, +) : StatusBarInitializerStore, CoreStartable { + + init { + StatusBarConnectedDisplays.assertInNewMode() + } + + private val perDisplayInitializers = ConcurrentHashMap<Int, StatusBarInitializer>() + + override val defaultDisplay: StatusBarInitializer + get() = forDisplay(Display.DEFAULT_DISPLAY) + + override fun forDisplay(displayId: Int): StatusBarInitializer { + if (displayRepository.getDisplay(displayId) == null) { + throw IllegalArgumentException("Display with id $displayId doesn't exist.") + } + return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) } + } + + override fun start() { + backgroundApplicationScope.launch( + CoroutineName("MultiDisplayStatusBarInitializerStore#start") + ) { + displayRepository.displayRemovalEvent.collect { removedDisplayId -> + perDisplayInitializers.remove(removedDisplayId) + } + } + } +} + +@SysUISingleton +class SingleDisplayStatusBarInitializerStore +@Inject +constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore { + + init { + StatusBarConnectedDisplays.assertInLegacyMode() + } + + private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY) + + override val defaultDisplay: StatusBarInitializer = defaultInstance + + override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt index 8bd990b83a63..d372eb29b27b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.phone.AutoHideController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions import com.android.systemui.statusbar.phone.PhoneStatusBarViewController -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.statusbar.window.data.model.StatusBarWindowState import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore import com.android.wm.shell.bubbles.Bubbles @@ -63,8 +63,8 @@ class StatusBarOrchestrator constructor( @Application private val applicationScope: CoroutineScope, private val statusBarInitializer: StatusBarInitializer, - private val statusBarWindowController: StatusBarWindowController, private val statusBarModeRepository: StatusBarModeRepositoryStore, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val demoModeController: DemoModeController, private val pluginDependencyProvider: PluginDependencyProvider, private val autoHideController: AutoHideController, @@ -157,7 +157,7 @@ constructor( private fun createAndAddWindow() { initializeStatusBarFragment() - statusBarWindowController.attach() + statusBarWindowControllerStore.defaultDisplay.attach() } private fun initializeStatusBarFragment() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index cd1642eee4b4..5aad11fe1034 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -77,16 +77,6 @@ abstract class StatusBarModule { @Provides @SysUISingleton - fun defaultStatusBarWindowController( - context: Context, - viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, - factory: StatusBarWindowControllerImpl.Factory, - ): StatusBarWindowController { - return factory.create(context, viewCaptureAwareWindowManager) - } - - @Provides - @SysUISingleton fun windowControllerStore( multiDisplayImplLazy: Lazy<MultiDisplayStatusBarWindowControllerStore>, singleDisplayImplLazy: Lazy<SingleDisplayStatusBarWindowControllerStore>, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 118f5f0515be..bf7e879bb72f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -34,7 +34,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.res.R import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.animation.AnimationUtil.Companion.frames import javax.inject.Inject import kotlin.math.roundToInt @@ -44,8 +44,8 @@ import kotlin.math.roundToInt */ class SystemEventChipAnimationController @Inject constructor( private val context: Context, - private val statusBarWindowController: StatusBarWindowController, - private val contentInsetsProvider: StatusBarContentInsetsProvider + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, + private val contentInsetsProvider: StatusBarContentInsetsProvider, ) : SystemStatusAnimationCallback { private lateinit var animationWindowView: FrameLayout @@ -244,7 +244,7 @@ class SystemEventChipAnimationController @Inject constructor( val height = themedContext.resources.getDimensionPixelSize(R.dimen.status_bar_height) val lp = FrameLayout.LayoutParams(MATCH_PARENT, height) lp.gravity = Gravity.END or Gravity.TOP - statusBarWindowController.addViewToWindow(animationWindowView, lp) + statusBarWindowControllerStore.defaultDisplay.addViewToWindow(animationWindowView, lp) animationWindowView.clipToPadding = false animationWindowView.clipChildren = false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt index f0e60dd2ce54..e34f61df8e88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt @@ -23,7 +23,7 @@ import androidx.core.animation.AnimatorListenerAdapter import androidx.core.animation.AnimatorSet import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dump.DumpManager -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.Assert import com.android.systemui.util.time.SystemClock import java.io.PrintWriter @@ -65,7 +65,7 @@ open class SystemStatusAnimationSchedulerImpl constructor( private val coordinator: SystemEventCoordinator, private val chipAnimationController: SystemEventChipAnimationController, - private val statusBarWindowController: StatusBarWindowController, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, dumpManager: DumpManager, private val systemClock: SystemClock, @Application private val coroutineScope: CoroutineScope, @@ -277,7 +277,7 @@ constructor( private fun runChipAppearAnimation() { Assert.isMainThread() if (hasPersistentDot) { - statusBarWindowController.setForceStatusBarVisible(true) + statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(true) } animationState.value = ANIMATING_IN @@ -311,7 +311,7 @@ constructor( scheduledEvent.value != null -> ANIMATION_QUEUED else -> IDLE } - statusBarWindowController.setForceStatusBarVisible(false) + statusBarWindowControllerStore.defaultDisplay.setForceStatusBarVisible(false) } } ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt index 693ae6617feb..ebaa3c436de4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt @@ -20,7 +20,7 @@ import android.content.Context import android.view.MotionEvent import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.DisplayTracker -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import javax.inject.Inject /** A class to detect when a user swipes away the status bar. */ @@ -31,10 +31,11 @@ constructor( context: Context, displayTracker: DisplayTracker, logger: SwipeUpGestureLogger, - private val statusBarWindowController: StatusBarWindowController, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, ) : SwipeUpGestureHandler(context, displayTracker, logger, loggerTag = LOGGER_TAG) { override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean { // Gesture starts just below the status bar + val statusBarWindowController = statusBarWindowControllerStore.defaultDisplay return ev.y >= statusBarWindowController.statusBarHeight && ev.y <= 3 * statusBarWindowController.statusBarHeight } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index c52632e4382e..9d5d7a19916a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -275,7 +275,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { } } - if (LockscreenOtpRedaction.isEnabled()) { + if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { + if (inflaterParams.isChildInGroup() && needsRedaction) { params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index ca5f49d28823..684ce48d9c7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -88,6 +88,7 @@ import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.ZenModesCleanupStartable; import dagger.Binds; import dagger.Module; @@ -299,4 +300,10 @@ public interface NotificationsModule { ZenModeRepository repository) { return new NotificationsSoundPolicyInteractor(repository); } + + /** Binds {@link ZenModesCleanupStartable} as a {@link CoreStartable}. */ + @Binds + @IntoMap + @ClassKey(ZenModesCleanupStartable.class) + CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt index 9b382e61b2e3..697a6ce52ba9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel @@ -26,6 +27,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -74,6 +76,17 @@ constructor( val allNotificationsCountValue: Int get() = repository.activeNotifications.value.individuals.size + /** The notifications that are promoted and ongoing. Sorted by priority order. */ + val promotedOngoingNotifications: Flow<List<ActiveNotificationModel>> = + if (StatusBarNotifChips.isEnabled) { + // TODO(b/364653005): Filter all the notifications down to just the promoted ones. + // TODO(b/364653005): [ongoingCallNotification] should be incorporated into this flow + // instead of being separate. + topLevelRepresentativeNotifications + } else { + flowOf(emptyList()) + } + /** * The priority ongoing call notification, or null if there is no ongoing call. * diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt index 695e088b5f8b..fbec6406e9d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel import android.content.Context import android.icu.text.MessageFormat -import com.android.app.tracing.coroutines.flow.flowOn import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager import com.android.systemui.modes.shared.ModesUi @@ -40,6 +39,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 560028cb5640..4c7d90c12839 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -830,4 +830,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView }); } } + + protected void dumpAppearAnimationProperties(IndentingPrintWriter pw, String[] args) { + pw.print("AppearAnimation: "); + pw.print("mDrawingAppearAnimation", mDrawingAppearAnimation); + pw.print("mAppearAnimationFraction", mAppearAnimationFraction); + pw.print("mIsHeadsUpAnimation", mIsHeadsUpAnimation); + pw.print("mTargetPoint", mTargetPoint); + pw.println(); + } } 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 adb3352b948c..33dbbc233fa9 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 @@ -1015,7 +1015,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } mNotificationParent = isChildInGroup ? parent : null; mPrivateLayout.setIsChildInGroup(isChildInGroup); - if (LockscreenOtpRedaction.isEnabled()) { + if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { mPublicLayout.setIsChildInGroup(isChildInGroup); } @@ -3883,6 +3883,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView dumpHeights(pw); } showingLayout.dump(pw, args); + dumpAppearAnimationProperties(pw, args); dumpCustomOutline(pw, args); dumpClipping(pw, args); if (getViewState() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt index b90aa107d617..71f9c07ba2d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactoryContainer.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row import android.widget.flags.Flags.notifLinearlayoutOptimized import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory import com.android.systemui.statusbar.notification.shared.NotificationViewFlipperPausing import javax.inject.Inject import javax.inject.Provider @@ -35,6 +36,7 @@ constructor( bigPictureLayoutInflaterFactory: BigPictureLayoutInflaterFactory, optimizedLinearLayoutFactory: NotificationOptimizedLinearLayoutFactory, notificationViewFlipperFactory: Provider<NotificationViewFlipperFactory>, + notificationRowIconViewInflaterFactory: NotificationRowIconViewInflaterFactory, ) : NotifRemoteViewsFactoryContainer { override val factories: Set<NotifRemoteViewsFactory> = buildSet { add(precomputedTextViewFactory) @@ -47,5 +49,8 @@ constructor( if (NotificationViewFlipperPausing.isEnabled) { add(notificationViewFlipperFactory.get()) } + if (android.app.Flags.notificationsRedesignAppIcons()) { + add(notificationRowIconViewInflaterFactory) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 69f45db40c65..41abac1d47f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -223,7 +223,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder ); } - if (LockscreenOtpRedaction.isEnabled()) { + if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { result.mPublicInflatedSingleLineViewModel = SingleLineViewInflater.inflateRedactedSingleLineViewModel(row.getContext(), isConversation); @@ -309,7 +309,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder }); break; case FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE: - if (LockscreenOtpRedaction.isEnabled()) { + if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { row.getPublicLayout() .performWhenContentInactive(VISIBLE_TYPE_SINGLELINE, () -> { row.getPublicLayout().setSingleLineView(null); @@ -342,7 +342,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder * Cancel any pending content view frees from {@link #freeNotificationView} for the provided * content views. * - * @param row top level notification row containing the content views + * @param row top level notification row containing the content views * @param contentViews content views to cancel pending frees on */ private void cancelContentViewFrees( @@ -360,7 +360,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((contentViews & FLAG_CONTENT_VIEW_PUBLIC) != 0) { row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_CONTRACTED); } - if (LockscreenOtpRedaction.isEnabled() + if (LockscreenOtpRedaction.isSingleLineViewEnabled() && (contentViews & FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) != 0) { row.getPublicLayout().removeContentInactiveRunnable(VISIBLE_TYPE_SINGLELINE); } @@ -478,6 +478,13 @@ public class NotificationContentInflater implements NotificationRowContentBinder notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP)); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)); + if (android.app.Flags.notificationsRedesignAppIcons()) { + setRemoteViewsInflaterFactory(result.mNewGroupHeaderView, + notifLayoutInflaterFactoryProvider.provide(row, FLAG_GROUP_SUMMARY_HEADER)); + setRemoteViewsInflaterFactory(result.mNewMinimizedGroupHeaderView, + notifLayoutInflaterFactoryProvider.provide(row, + FLAG_LOW_PRIORITY_GROUP_SUMMARY_HEADER)); + } } private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews, @@ -516,6 +523,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder logger.logAsyncTaskProgress(entry, "contracted view applied"); result.inflatedContentView = v; } + @Override public RemoteViews getRemoteView() { return result.newContentView; @@ -974,7 +982,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder } } - if (LockscreenOtpRedaction.isEnabled() + if (LockscreenOtpRedaction.isSingleLineViewEnabled() && (reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE) != 0) { HybridNotificationView view = result.mPublicInflatedSingleLineView; SingleLineViewModel viewModel = result.mPublicInflatedSingleLineViewModel; @@ -1254,7 +1262,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder ); } - if (LockscreenOtpRedaction.isEnabled()) { + if (LockscreenOtpRedaction.isSingleLineViewEnabled()) { result.mPublicInflatedSingleLineViewModel = SingleLineViewInflater.inflateRedactedSingleLineViewModel(mContext, isConversation); @@ -1406,6 +1414,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder @VisibleForTesting abstract static class ApplyCallback { public abstract void setResultView(View v); + public abstract RemoteViews getRemoteView(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index 84f2f6670839..43ade5cc5a7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.row; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.statusbar.notification.row.icon.AppIconProviderModule; import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor; import dagger.Binds; @@ -28,7 +29,7 @@ import javax.inject.Provider; /** * Dagger Module containing notification row and view inflation implementations. */ -@Module +@Module(includes = {AppIconProviderModule.class}) public abstract class NotificationRowModule { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt new file mode 100644 index 000000000000..24b5cf1aa33b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.app.ActivityManager +import android.app.Flags +import android.content.Context +import android.content.pm.PackageManager.NameNotFoundException +import android.graphics.Color +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable +import android.util.Log +import com.android.internal.R +import com.android.launcher3.icons.BaseIconFactory +import com.android.systemui.dagger.SysUISingleton +import dagger.Module +import dagger.Provides +import javax.inject.Inject +import javax.inject.Provider + +/** A provider used to cache and fetch app icons used by notifications. */ +interface AppIconProvider { + @Throws(NameNotFoundException::class) + fun getOrFetchAppIcon(packageName: String, context: Context): Drawable +} + +@SysUISingleton +class AppIconProviderImpl @Inject constructor(private val sysuiContext: Context) : AppIconProvider { + private val iconFactory: BaseIconFactory + get() { + val isLowRam = ActivityManager.isLowRamDeviceStatic() + val res = sysuiContext.resources + val iconSize: Int = + res.getDimensionPixelSize( + if (isLowRam) R.dimen.notification_small_icon_size_low_ram + else R.dimen.notification_small_icon_size + ) + return BaseIconFactory(sysuiContext, res.configuration.densityDpi, iconSize) + } + + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + val icon = context.packageManager.getApplicationIcon(packageName) + return BitmapDrawable( + context.resources, + iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_HARDWARE), + ) + } +} + +class NoOpIconProvider : AppIconProvider { + companion object { + const val TAG = "NoOpIconProvider" + } + + override fun getOrFetchAppIcon(packageName: String, context: Context): Drawable { + Log.wtf(TAG, "NoOpIconProvider should not be used anywhere.") + return ColorDrawable(Color.WHITE) + } +} + +@Module +class AppIconProviderModule { + @Provides + @SysUISingleton + fun provideImpl(realImpl: Provider<AppIconProviderImpl>): AppIconProvider = + if (Flags.notificationsRedesignAppIcons()) { + realImpl.get() + } else { + NoOpIconProvider() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.kt new file mode 100644 index 000000000000..2522e58a08a5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationRowIconViewInflaterFactory.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.statusbar.notification.row.icon + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.View +import com.android.internal.widget.NotificationRowIconView +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.NotifRemoteViewsFactory +import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder +import javax.inject.Inject + +/** + * A factory which owns the construction of any NotificationRowIconView inside of Notifications in + * SystemUI. This allows overriding the small icon with the app icon in notifications. + */ +class NotificationRowIconViewInflaterFactory +@Inject +constructor(private val appIconProvider: AppIconProvider) : NotifRemoteViewsFactory { + override fun instantiate( + row: ExpandableNotificationRow, + @NotificationRowContentBinder.InflationFlag layoutType: Int, + parent: View?, + name: String, + context: Context, + attrs: AttributeSet, + ): View? { + return when (name) { + NotificationRowIconView::class.java.name -> + NotificationRowIconView(context, attrs).also { view -> + val sbn = row.entry.sbn + view.setIconProvider( + object : NotificationRowIconView.NotificationIconProvider { + override fun shouldShowAppIcon(): Boolean { + // TODO(b/371174789): implement me + return true + } + + override fun getAppIcon(): Drawable { + return appIconProvider.getOrFetchAppIcon(sbn.packageName, context) + } + } + ) + } + else -> null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt index 078deb9f7c92..da4a126a42c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/LockscreenOtpRedaction.kt @@ -30,6 +30,9 @@ object LockscreenOtpRedaction { @JvmStatic inline val isEnabled - get() = - redactSensitiveContentNotificationsOnLockscreen() && AsyncHybridViewInflation.isEnabled + get() = redactSensitiveContentNotificationsOnLockscreen() + + @JvmStatic + inline val isSingleLineViewEnabled + get() = isEnabled && AsyncHybridViewInflation.isEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationProgressTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationProgressTemplateViewWrapper.kt new file mode 100644 index 000000000000..a693fd397801 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationProgressTemplateViewWrapper.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification.row.wrapper + +import android.content.Context +import android.view.View +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow + +/** Wraps a notification containing a progress template */ +class NotificationProgressTemplateViewWrapper( + ctx: Context, + view: View, + row: ExpandableNotificationRow, +) : NotificationTemplateViewWrapper(ctx, view, row) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 22b95efa5a95..182fba34cafb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -76,6 +76,8 @@ public abstract class NotificationViewWrapper implements TransformableView { return new NotificationCompactHeadsUpTemplateViewWrapper(ctx, v, row); } else if ("compactMessagingHUN".equals((v.getTag()))) { return new NotificationCompactMessagingTemplateViewWrapper(ctx, v, row); + } else if ("progress".equals(v.getTag())) { + return new NotificationProgressTemplateViewWrapper(ctx, v, row); } if (row.getEntry().getSbn().getNotification().isStyle( 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 00c5c40fc8ac..f3437b5732ae 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 @@ -2098,8 +2098,13 @@ public class NotificationStackScrollLayoutController implements Dumpable { } class TouchHandler implements Gefingerpoken { + private boolean mSwipeWantsIt = false; + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + // Reset on each call to intercept, and share swipe state with onTouchEvent() + // below when this method returns true. + mSwipeWantsIt = false; mView.initDownStates(ev); mView.handleEmptySpaceClick(ev); @@ -2126,17 +2131,16 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.startDraggingOnHun(); } } - boolean swipeWantsIt = false; if (mLongPressedView == null && !mView.isBeingDragged() && !mView.isExpandingNotification() && !mView.getExpandedInThisMotion() && !mView.getOnlyScrollingInThisMotion() && !mView.getDisallowDismissInThisMotion()) { - swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev); + mSwipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev); } // Check if we need to clear any snooze leavebehinds boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; - if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && + if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !mSwipeWantsIt && !expandWantsIt && !scrollWantsIt) { mView.setCheckForLeaveBehind(false); mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, @@ -2155,7 +2159,8 @@ public class NotificationStackScrollLayoutController implements Dumpable { && ev.getActionMasked() != MotionEvent.ACTION_DOWN) { mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING); } - return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt || hunWantsIt; + return mSwipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt || + hunWantsIt; } @Override @@ -2192,7 +2197,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { } } } - boolean horizontalSwipeWantsIt = false; + boolean horizontalSwipeWantsIt = mSwipeWantsIt; boolean scrollerWantsIt = false; // NOTE: the order of these is important. If reversed, onScrollTouch will reset on an // UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes. @@ -2201,7 +2206,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { && !mView.getExpandedInThisMotion() && !onlyScrollingInThisMotion && !mView.getDisallowDismissInThisMotion()) { - horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev); + mSwipeHelper.onTouchEvent(ev); } if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt index 87d70ba12012..fb42ee7908b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt @@ -17,7 +17,8 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder import android.util.Log -import com.android.app.tracing.coroutines.flow.filter +import com.android.app.tracing.coroutines.flow.collectTraced +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.view.onLayoutChanged import com.android.systemui.dagger.SysUISingleton @@ -37,7 +38,6 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch /** Binds the [NotificationScrollView]. */ @SysUISingleton @@ -79,39 +79,39 @@ constructor( launch { viewModel .shadeScrimShape(cornerRadius = scrimRadius, viewLeftOffset = viewLeftOffset) - .collect { view.setScrimClippingShape(it) } + .collectTraced { view.setScrimClippingShape(it) } } - launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } } - launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } } + launch { viewModel.maxAlpha.collectTraced { view.setMaxAlpha(it) } } + launch { viewModel.scrolledToTop.collectTraced { view.setScrolledToTop(it) } } launch { - viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } + viewModel.expandFraction.collectTraced { view.setExpandFraction(it.coerceIn(0f, 1f)) } } - launch { viewModel.qsExpandFraction.collect { view.setQsExpandFraction(it) } } + launch { viewModel.qsExpandFraction.collectTraced { view.setQsExpandFraction(it) } } launch { - viewModel.isShowingStackOnLockscreen.collect { + viewModel.isShowingStackOnLockscreen.collectTraced { view.setShowingStackOnLockscreen(it) } } launch { - viewModel.alphaForLockscreenFadeIn.collect { view.setAlphaForLockscreenFadeIn(it) } + viewModel.alphaForLockscreenFadeIn.collectTraced { view.setAlphaForLockscreenFadeIn(it) } } - launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } } - launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } } + launch { viewModel.isScrollable.collectTraced { view.setScrollingEnabled(it) } } + launch { viewModel.isDozing.collectTraced { isDozing -> view.setDozing(isDozing) } } launch { - viewModel.isPulsing.collect { isPulsing -> + viewModel.isPulsing.collectTraced { isPulsing -> view.setPulsing(isPulsing, viewModel.shouldAnimatePulse.value) } } launch { viewModel.shouldResetStackTop .filter { it } - .collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } + .collectTraced { view.setStackTop(-(view.getHeadsUpInset().toFloat())) } } launch { - viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() } + viewModel.shouldCloseGuts.filter { it }.collectTraced { view.closeGutsOnSceneTouch() } } - launch { viewModel.suppressHeightUpdates.collect { view.suppressHeightUpdates(it) } } + launch { viewModel.suppressHeightUpdates.collectTraced { view.suppressHeightUpdates(it) } } launchAndDispose { view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index f39af18afcea..878ae91391e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel import androidx.annotation.VisibleForTesting +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.dagger.SysUISingleton @@ -158,6 +159,7 @@ constructor( ) { shadeExpansion, qsExpansion -> shadeExpansion || qsExpansion } + .flowName("isAnyExpanded") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -175,6 +177,7 @@ constructor( isAnyExpanded -> isShadeLocked && isAnyExpanded } + .flowName("isShadeLocked") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -227,6 +230,7 @@ constructor( ), keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f }, ) + .flowName("isOnLockscreen") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -239,6 +243,7 @@ constructor( combine(isOnLockscreen, isAnyExpanded) { isKeyguard, isAnyExpanded -> isKeyguard && !isAnyExpanded } + .flowName("isOnLockscreenWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -274,6 +279,7 @@ constructor( combine(isOnGlanceableHub, isAnyExpanded) { isGlanceableHub, isAnyExpanded -> isGlanceableHub && !isAnyExpanded } + .flowName("isOnGlanceableHubWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -288,6 +294,7 @@ constructor( isAnyExpanded -> isDreaming && !isAnyExpanded } + .flowName("isDreamingWithoutShade") .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -345,6 +352,7 @@ constructor( } } } + .flowName("shadeCollapseFadeIn") .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), @@ -382,6 +390,7 @@ constructor( bounds.copy(top = top, isAnimated = animate) } } + .flowName("bounds") .stateIn( scope = applicationScope, started = SharingStarted.Lazily, @@ -495,6 +504,7 @@ constructor( // flatMapLatest below, the last value gets emitted, to avoid the randomness of `merge`. val alphaForTransitionsAndShade = merge(alphaForTransitions(viewState), alphaForShadeAndQsExpansion) + .flowName("alphaForTransitionsAndShade") .stateIn( // Use view-level scope instead of ApplicationScope, to prevent collection that // never stops 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 5b37468c9da6..d1338eadb6b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt @@ -58,7 +58,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.kotlin.getOrNull @@ -93,7 +93,7 @@ constructor( @Main private val mainExecutor: DelayableExecutor, private val shadeControllerLazy: Lazy<ShadeController>, private val communalSceneInteractor: CommunalSceneInteractor, - private val statusBarWindowController: StatusBarWindowController, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>, private val shadeAnimationInteractor: ShadeAnimationInteractor, private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, @@ -562,7 +562,7 @@ constructor( } val rootView = animationController.transitionContainer.rootView val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> = - statusBarWindowController.wrapAnimationControllerIfInStatusBar( + statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar( rootView, animationController ) 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 7e5b45543e9e..3d7cd9c9fbcd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -128,7 +128,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.emergency.EmergencyGestureModule.EmergencyGestureIntentFactory; import com.android.systemui.flags.FeatureFlags; @@ -226,7 +225,7 @@ import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; -import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape; import com.android.systemui.util.DumpUtilsKt; @@ -372,7 +371,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING; private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarInitializer mStatusBarInitializer; - private final StatusBarWindowController mStatusBarWindowController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; private final StatusBarModeRepositoryStore mStatusBarModeRepository; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @VisibleForTesting @@ -610,7 +609,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { LightBarController lightBarController, AutoHideController autoHideController, StatusBarInitializer statusBarInitializer, - StatusBarWindowController statusBarWindowController, + StatusBarWindowControllerStore statusBarWindowControllerStore, StatusBarWindowStateController statusBarWindowStateController, StatusBarModeRepositoryStore statusBarModeRepository, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -716,7 +715,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mLightBarController = lightBarController; mAutoHideController = autoHideController; mStatusBarInitializer = statusBarInitializer; - mStatusBarWindowController = statusBarWindowController; + mStatusBarWindowControllerStore = statusBarWindowControllerStore; mStatusBarModeRepository = statusBarModeRepository; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mPulseExpansionHandler = pulseExpansionHandler; @@ -1894,7 +1893,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // When the StatusBarSimpleFragment flag is enabled, this logic will be done in // StatusBarOrchestrator if (!StatusBarSimpleFragment.isEnabled()) { - mStatusBarWindowController.attach(); + mStatusBarWindowControllerStore.getDefaultDisplay().attach(); } } @@ -2825,23 +2824,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScrimController.setExpansionAffectsAlpha(!unlocking); if (mAlternateBouncerInteractor.isVisibleState()) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) - && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED - || mTransitionToFullShadeProgress > 0f)) { - // Assume scrim state for shade is already correct and do nothing - } else { - // Safeguard which prevents the scrim from being stuck in the wrong state - mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); - } + if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) + && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED + || mTransitionToFullShadeProgress > 0f)) { + // Assume scrim state for shade is already correct and do nothing } else { - if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded()) - && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED - || mTransitionToFullShadeProgress > 0f)) { - mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE); - } else { - mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED); - } + // Safeguard which prevents the scrim from being stuck in the wrong state + mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); } // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. @@ -3168,12 +3157,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { public void onDozeAmountChanged(float linear, float eased) { if (!lightRevealMigration() && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - // If wakeAndUnlocking, this is handled in AuthRippleInteractor - if (!mBiometricUnlockController.isWakeAndUnlock()) { - mLightRevealScrim.setRevealAmount(1f - linear); - } - } else { + // If wakeAndUnlocking, this is handled in AuthRippleInteractor + if (!mBiometricUnlockController.isWakeAndUnlock()) { mLightRevealScrim.setRevealAmount(1f - linear); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index 316e1f13bc2b..a34ac2e11c2e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -22,7 +22,7 @@ import android.content.res.Resources import android.hardware.biometrics.BiometricSourceType import android.provider.Settings import com.android.app.tracing.ListenersTracing.forEachTraced -import com.android.app.tracing.coroutines.launch +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application 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 460423378dff..1cca3ae0a2c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt @@ -57,7 +57,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.kotlin.getOrNull import dagger.Lazy @@ -85,7 +85,7 @@ constructor( private val context: Context, @DisplayId private val displayId: Int, private val lockScreenUserManager: NotificationLockscreenUserManager, - private val statusBarWindowController: StatusBarWindowController, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val wakefulnessLifecycle: WakefulnessLifecycle, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val deviceProvisionedController: DeviceProvisionedController, @@ -525,7 +525,7 @@ constructor( } val rootView = animationController.transitionContainer.rootView val controllerFromStatusBar: Optional<ActivityTransitionAnimator.Controller> = - statusBarWindowController.wrapAnimationControllerIfInStatusBar( + statusBarWindowControllerStore.defaultDisplay.wrapAnimationControllerIfInStatusBar( rootView, animationController ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index d6716a0c4701..e7d9717defa7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -40,7 +40,7 @@ import com.android.systemui.Flags; import com.android.systemui.Gefingerpoken; import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; -import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder; import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel; import com.android.systemui.util.leak.RotationUtils; @@ -49,7 +49,7 @@ import java.util.Objects; public class PhoneStatusBarView extends FrameLayout { private static final String TAG = "PhoneStatusBarView"; - private final StatusBarWindowController mStatusBarWindowController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; private int mRotationOrientation = -1; @Nullable @@ -75,7 +75,7 @@ public class PhoneStatusBarView extends FrameLayout { public PhoneStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); - mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); + mStatusBarWindowControllerStore = Dependency.get(StatusBarWindowControllerStore.class); } void setTouchEventHandler(Gefingerpoken handler) { @@ -326,7 +326,7 @@ public class PhoneStatusBarView extends FrameLayout { if (Flags.statusBarStopUpdatingWindowHeight()) { return; } - mStatusBarWindowController.refreshStatusBarHeight(); + mStatusBarWindowControllerStore.getDefaultDisplay().refreshStatusBarHeight(); } interface HasCornerCutoutFetcher { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 069c6241491c..40e52935daf1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -1715,26 +1715,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump updateScrims(); } - public void setHasBackdrop(boolean hasBackdrop) { - for (ScrimState state : ScrimState.values()) { - state.setHasBackdrop(hasBackdrop); - } - - // Backdrop event may arrive after state was already applied, - // in this case, back-scrim needs to be re-evaluated - if (mState == ScrimState.AOD || mState == ScrimState.PULSING) { - float newBehindAlpha = mState.getBehindAlpha(); - if (isNaN(newBehindAlpha)) { - throw new IllegalStateException("Scrim opacity is NaN for state: " + mState - + ", back: " + mBehindAlpha); - } - if (mBehindAlpha != newBehindAlpha) { - mBehindAlpha = newBehindAlpha; - updateScrims(); - } - } - } - private void setKeyguardFadingAway(boolean fadingAway, long duration) { for (ScrimState state : ScrimState.values()) { state.setKeyguardFadingAway(fadingAway, duration); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index fbba3dc107f1..a0ab6120f372 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -210,7 +210,7 @@ public enum ScrimState { @Override public float getMaxLightRevealScrimAlpha() { - return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f; + return mWallpaperSupportsAmbientMode ? 0f : 1f; } @Override @@ -369,7 +369,6 @@ public enum ScrimState { DockManager mDockManager; boolean mDisplayRequiresBlanking; boolean mWallpaperSupportsAmbientMode; - boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; boolean mOccludeAnimationPlaying; boolean mWakeLockScreenSensorActive; @@ -489,10 +488,6 @@ public enum ScrimState { return false; } - public void setHasBackdrop(boolean hasBackdrop) { - mHasBackdrop = hasBackdrop; - } - public void setWakeLockScreenSensorActive(boolean active) { mWakeLockScreenSensorActive = active; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java index 8f2d4f931b91..7145ffe1f515 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -28,7 +28,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; -import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import javax.inject.Inject; @@ -38,7 +38,7 @@ import javax.inject.Inject; @SysUISingleton public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, CoreStartable { private final NotificationShadeWindowController mNotificationShadeWindowController; - private final StatusBarWindowController mStatusBarWindowController; + private final StatusBarWindowControllerStore mStatusBarWindowControllerStore; private final ShadeViewController mShadeViewController; private final PanelExpansionInteractor mPanelExpansionInteractor; private final NotificationStackScrollLayoutController mNsslController; @@ -50,7 +50,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, @Inject StatusBarHeadsUpChangeListener( NotificationShadeWindowController notificationShadeWindowController, - StatusBarWindowController statusBarWindowController, + StatusBarWindowControllerStore statusBarWindowControllerStore, ShadeViewController shadeViewController, PanelExpansionInteractor panelExpansionInteractor, NotificationStackScrollLayoutController nsslController, @@ -59,7 +59,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, StatusBarStateController statusBarStateController, NotificationRemoteInputManager notificationRemoteInputManager) { mNotificationShadeWindowController = notificationShadeWindowController; - mStatusBarWindowController = statusBarWindowController; + mStatusBarWindowControllerStore = statusBarWindowControllerStore; mShadeViewController = shadeViewController; mPanelExpansionInteractor = panelExpansionInteractor; mNsslController = nsslController; @@ -78,7 +78,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { if (inPinnedMode) { mNotificationShadeWindowController.setHeadsUpShowing(true); - mStatusBarWindowController.setForceStatusBarVisible(true); + mStatusBarWindowControllerStore.getDefaultDisplay().setForceStatusBarVisible(true); if (mPanelExpansionInteractor.isFullyCollapsed()) { mShadeViewController.updateTouchableRegion(); } @@ -93,7 +93,9 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener, // open artificially. mNotificationShadeWindowController.setHeadsUpShowing(false); if (bypassKeyguard) { - mStatusBarWindowController.setForceStatusBarVisible(false); + mStatusBarWindowControllerStore + .getDefaultDisplay() + .setForceStatusBarVisible(false); } } else { // we need to keep the panel open artificially, let's wait until the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 17bd53869ee5..74c6e72d3400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -47,7 +47,6 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; @@ -68,7 +67,6 @@ import com.android.systemui.bouncer.util.BouncerTestUtilsKt; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor; -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -77,9 +75,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInte import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.DismissAction; -import com.android.systemui.keyguard.shared.model.Edge; import com.android.systemui.keyguard.shared.model.KeyguardDone; -import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; @@ -165,7 +161,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DreamOverlayStateController mDreamOverlayStateController; @Nullable private final FoldAodAnimationController mFoldAodAnimationController; - KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController; private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor; private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final AlternateBouncerInteractor mAlternateBouncerInteractor; @@ -463,11 +458,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb onPanelExpansionChanged(currentState); } mNotificationContainer = notificationContainer; - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create( - centralSurfaces.getKeyguardMessageArea()); - } - mCentralSurfacesRegistered = true; registerListeners(); @@ -518,24 +508,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer.cancel(null); } mListenForCanShowAlternateBouncer = null; - if (!DeviceEntryUdfpsRefactor.isEnabled()) { - mListenForAlternateBouncerTransitionSteps = mJavaAdapter.alwaysCollectFlow( - mKeyguardTransitionInteractor - .transition(Edge.create(KeyguardState.ALTERNATE_BOUNCER)), - this::consumeFromAlternateBouncerTransitionSteps - ); - - mListenForKeyguardAuthenticatedBiometricsHandled = mJavaAdapter.alwaysCollectFlow( - mPrimaryBouncerInteractor.getKeyguardAuthenticatedBiometricsHandled(), - this::consumeKeyguardAuthenticatedBiometricsHandled - ); - } else { - // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. - mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( - mAlternateBouncerInteractor.getCanShowAlternateBouncer(), - this::consumeCanShowAlternateBouncer - ); - } + // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. + mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( + mAlternateBouncerInteractor.getCanShowAlternateBouncer(), + this::consumeCanShowAlternateBouncer + ); if (KeyguardWmStateRefactor.isEnabled()) { // Show the keyguard views whenever we've told WM that the lockscreen is visible. @@ -792,21 +769,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { - Log.d(TAG, "showBouncer:alternateBouncer.forceShow()"); - mAlternateBouncerInteractor.forceShow(); - updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); - } else { - showPrimaryBouncer(scrimmed); - } - return; - } - - if (!mAlternateBouncerInteractor.show()) { - showPrimaryBouncer(scrimmed); - } else { + if (mAlternateBouncerInteractor.canShowAlternateBouncerForFingerprint()) { + Log.d(TAG, "showBouncer:alternateBouncer.forceShow()"); + mAlternateBouncerInteractor.forceShow(); updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); + } else { + showPrimaryBouncer(scrimmed); } } @@ -921,13 +889,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardGoneCancelAction = null; } - if (DeviceEntryUdfpsRefactor.isEnabled()) { - Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()"); - mAlternateBouncerInteractor.forceShow(); - updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); - } else { - updateAlternateBouncerShowing(mAlternateBouncerInteractor.show()); - } + Log.d(TAG, "dismissWithAction:alternateBouncer.forceShow()"); + mAlternateBouncerInteractor.forceShow(); + updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); setKeyguardMessage(message, null, null); return; } @@ -1033,11 +997,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } final boolean isShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState(); - if (mKeyguardMessageAreaController != null) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer); - mKeyguardMessageAreaController.setMessage(""); - } if (!SceneContainerFlag.isEnabled()) { mKeyguardUpdateManager.setAlternateBouncerShowing(isShowingAlternateBouncer); } @@ -1646,12 +1605,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** Display security message to relevant KeyguardMessageArea. */ public void setKeyguardMessage(String message, ColorStateList colorState, BiometricSourceType biometricSourceType) { - if (mAlternateBouncerInteractor.isVisibleState()) { - if (mKeyguardMessageAreaController != null) { - DeviceEntryUdfpsRefactor.assertInLegacyMode(); - mKeyguardMessageAreaController.setMessage(message, biometricSourceType); - } - } else { + if (!mAlternateBouncerInteractor.isVisibleState()) { mPrimaryBouncerInteractor.showMessage(message, colorState); } } @@ -1778,66 +1732,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } - /** - * An opportunity for the AlternateBouncer to handle the touch instead of sending - * the touch to NPVC child views. - * @return true if the alternate bouncer should consime the touch and prevent it from - * going to its child views - */ - public boolean dispatchTouchEvent(MotionEvent event) { - if (shouldInterceptTouchEvent(event) - && !mUdfpsOverlayInteractor.isTouchWithinUdfpsArea(event)) { - onTouch(event); - } - return shouldInterceptTouchEvent(event); - } - - /** - * Whether the touch should be intercepted by the AlternateBouncer before going to the - * notification shade's child views. - */ - public boolean shouldInterceptTouchEvent(MotionEvent event) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - return false; - } - return mAlternateBouncerInteractor.isVisibleState(); - } - - /** - * For any touches on the NPVC, show the primary bouncer if the alternate bouncer is currently - * showing. - */ - public boolean onTouch(MotionEvent event) { - if (DeviceEntryUdfpsRefactor.isEnabled()) { - return false; - } - - boolean handleTouch = shouldInterceptTouchEvent(event); - if (handleTouch) { - final boolean actionDown = event.getActionMasked() == MotionEvent.ACTION_DOWN; - final boolean actionDownThenUp = mAlternateBouncerInteractor.getReceivedDownTouch() - && event.getActionMasked() == MotionEvent.ACTION_UP; - final boolean udfpsOverlayWillForwardEventsOutsideNotificationShade = - mKeyguardUpdateManager.isUdfpsEnrolled(); - final boolean actionOutsideShouldDismissAlternateBouncer = - event.getActionMasked() == MotionEvent.ACTION_OUTSIDE - && !udfpsOverlayWillForwardEventsOutsideNotificationShade; - if (actionDown) { - mAlternateBouncerInteractor.setReceivedDownTouch(true); - } else if ((actionDownThenUp || actionOutsideShouldDismissAlternateBouncer) - && mAlternateBouncerInteractor.hasAlternateBouncerShownWithMinTime()) { - showPrimaryBouncer(true); - } - } - - // Forward NPVC touches to callbacks in case they want to respond to touches - for (KeyguardViewManagerCallback callback: mCallbacks) { - callback.onTouch(event); - } - - return handleTouch; - } - /** Update keyguard position based on a tapped X coordinate. */ public void updateKeyguardPosition(float x) { mPrimaryBouncerInteractor.setKeyguardPosition(x); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt index 5b0319883b5f..5f864e5dc53a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt @@ -15,12 +15,18 @@ */ package com.android.systemui.statusbar.phone.dagger +import android.view.Display import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Default import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.core.CommandQueueInitializer +import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore +import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.core.StatusBarInitializer import com.android.systemui.statusbar.core.StatusBarInitializerImpl +import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator import com.android.systemui.statusbar.core.StatusBarSimpleFragment import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks @@ -47,15 +53,31 @@ interface StatusBarPhoneModule { impl: CentralSurfacesCommandQueueCallbacks ): CommandQueue.Callbacks + @Binds + fun initializerFactory( + implFactory: StatusBarInitializerImpl.Factory + ): StatusBarInitializer.Factory + /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */ @Binds @IntoMap @ClassKey(StatusBarInitializerImpl::class) - fun bindStatusBarInitializer(impl: StatusBarInitializerImpl): CoreStartable + fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable - @Binds fun statusBarInitializer(impl: StatusBarInitializerImpl): StatusBarInitializer + @Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer companion object { + // Dagger doesn't support providing AssistedInject types, without a qualifier. Using the + // Default qualifier for this reason. + @Default + @Provides + @SysUISingleton + fun statusBarInitializerImpl( + implFactory: StatusBarInitializerImpl.Factory + ): StatusBarInitializerImpl { + return implFactory.create(displayId = Display.DEFAULT_DISPLAY) + } + @Provides @SysUISingleton @IntoMap @@ -83,5 +105,32 @@ interface StatusBarPhoneModule { CoreStartable.NOP } } + + @Provides + @SysUISingleton + @IntoMap + @ClassKey(StatusBarInitializerStore::class) + fun initializerStoreAsCoreStartable( + multiDisplayStoreLazy: Lazy<MultiDisplayStatusBarInitializerStore> + ): CoreStartable { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayStoreLazy.get() + } else { + CoreStartable.NOP + } + } + + @Provides + @SysUISingleton + fun initializerStore( + singleDisplayStoreLazy: Lazy<SingleDisplayStatusBarInitializerStore>, + multiDisplayStoreLazy: Lazy<MultiDisplayStatusBarInitializerStore>, + ): StatusBarInitializerStore { + return if (StatusBarConnectedDisplays.isEnabled) { + multiDisplayStoreLazy.get() + } else { + singleDisplayStoreLazy.get() + } + } } } 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 c258095510c6..35e30b8134bd 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 @@ -55,7 +55,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.chips.ron.shared.StatusBarRonChips; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; @@ -642,7 +642,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } boolean showSecondaryOngoingActivityChip = Flags.statusBarScreenSharingChips() - && StatusBarRonChips.isEnabled() + && StatusBarNotifChips.isEnabled() && mHasSecondaryOngoingActivity; return new StatusBarVisibilityModel( @@ -684,7 +684,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue boolean showSecondaryOngoingActivityChip = // Secondary chips are only supported when RONs are enabled. - StatusBarRonChips.isEnabled() + StatusBarNotifChips.isEnabled() && visibilityModel.getShowSecondaryOngoingActivityChip() && !disableNotifications; if (showSecondaryOngoingActivityChip) { @@ -811,7 +811,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void showSecondaryOngoingActivityChip(boolean animate) { - StatusBarRonChips.assertInNewMode(); + StatusBarNotifChips.assertInNewMode(); StatusBarSimpleFragment.assertInLegacyMode(); animateShow(mSecondaryOngoingActivityChip, animate); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index f026b99af49c..cf877a741d6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import dagger.Module; import dagger.Provides; @@ -125,9 +126,9 @@ public interface StatusBarFragmentModule { @StatusBarFragmentScope static PhoneStatusBarTransitions providePhoneStatusBarTransitions( @RootView PhoneStatusBarView view, - StatusBarWindowController statusBarWindowController - ) { - return new PhoneStatusBarTransitions(view, statusBarWindowController.getBackgroundView()); + StatusBarWindowControllerStore statusBarWindowControllerStore) { + return new PhoneStatusBarTransitions( + view, statusBarWindowControllerStore.getDefaultDisplay().getBackgroundView()); } /** */ 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 bd6a1c05ddc9..3cf8c3f48409 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 @@ -52,7 +52,7 @@ import com.android.systemui.statusbar.notification.shared.CallType 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.policy.CallbackController -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import java.util.concurrent.Executor @@ -75,7 +75,7 @@ constructor( @Main private val mainExecutor: Executor, private val iActivityManager: IActivityManager, private val dumpManager: DumpManager, - private val statusBarWindowController: StatusBarWindowController, + private val statusBarWindowControllerStore: StatusBarWindowControllerStore, private val swipeStatusBarAwayGestureHandler: SwipeStatusBarAwayGestureHandler, private val statusBarModeRepository: StatusBarModeRepositoryStore, @OngoingCallLog private val logger: LogBuffer, @@ -205,7 +205,9 @@ constructor( this.chipView = chipView val backgroundView: ChipBackgroundContainer? = chipView.findViewById(R.id.ongoing_activity_chip_background) - backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight } + backgroundView?.maxHeightFetcher = { + statusBarWindowControllerStore.defaultDisplay.statusBarHeight + } if (hasOngoingCall()) { updateChip() } @@ -339,7 +341,8 @@ constructor( // But, this class still needs to do the non-display logic regardless of the flag. uidObserver.registerWithUid(currentCallNotificationInfo.uid) if (!currentCallNotificationInfo.statusBarSwipedAway) { - statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(true) + statusBarWindowControllerStore.defaultDisplay + .setOngoingProcessRequiresStatusBarVisible(true) } updateGestureListening() sendStateChangeEvent() @@ -405,7 +408,9 @@ constructor( if (!Flags.statusBarScreenSharingChips()) { tearDownChipView() } - statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false) + statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible( + false + ) swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG) sendStateChangeEvent() uidObserver.unregister() @@ -429,7 +434,9 @@ constructor( private fun onSwipeAwayGestureDetected() { logger.log(TAG, LogLevel.DEBUG, {}, { "Swipe away gesture detected" }) callNotificationInfo = callNotificationInfo?.copy(statusBarSwipedAway = true) - statusBarWindowController.setOngoingProcessRequiresStatusBarVisible(false) + statusBarWindowControllerStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible( + false + ) swipeStatusBarAwayGestureHandler.removeOnGestureDetectedCallback(TAG) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt index bad6f80c3735..694a5e529ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel -import com.android.app.tracing.coroutines.createCoroutineTracingContext import androidx.annotation.VisibleForTesting +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlagsClassic @@ -29,8 +29,8 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.cancel @@ -116,7 +116,7 @@ constructor( private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> { // Create a child scope so we can cancel it - val vmScope = scope.createChildScope(createCoroutineTracingContext("MobileIconViewModel")) + val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel")) val vm = MobileIconViewModel( subId, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index 9c168be0693f..3a07d9b6beaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag +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.StatusBarSimpleFragment @@ -83,7 +84,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - if (Flags.statusBarScreenSharingChips() && !Flags.statusBarRonChips()) { + if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) launch { @@ -119,7 +120,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } - if (Flags.statusBarScreenSharingChips() && Flags.statusBarRonChips()) { + if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) { val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary) val secondaryChipView: View = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.kt new file mode 100644 index 000000000000..32b476b07d90 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModesCleanupStartable.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.statusbar.policy + +import android.app.NotificationManager +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.modes.shared.ModesUi +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +/** + * Cleanup task that deletes the obsolete "Gaming" AutomaticZenRule that was created by SystemUI in + * the faraway past, and still exists on some devices through upgrades or B&R. + */ +// TODO: b/372874878 - Remove this thing once it has run long enough +class ZenModesCleanupStartable +@Inject +constructor( + @Application private val applicationCoroutineScope: CoroutineScope, + @Background private val bgContext: CoroutineContext, + val notificationManager: NotificationManager, +) : CoreStartable { + + override fun start() { + if (!ModesUi.isEnabled) { + return + } + applicationCoroutineScope.launch { deleteObsoleteGamingMode() } + } + + private suspend fun deleteObsoleteGamingMode() { + withContext(bgContext) { + val allRules = notificationManager.automaticZenRules + val gamingModeEntry = + allRules.entries.firstOrNull { entry -> + entry.value.packageName == "com.android.systemui" && + entry.value.conditionId?.toString() == + "android-app://com.android.systemui/game-mode-dnd-controller" + } + if (gamingModeEntry != null) { + notificationManager.removeAutomaticZenRule(gamingModeEntry.key) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt index 5f30b3719aa7..7d0dadcf8c6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.window +import android.content.Context import android.view.Display import android.view.WindowManager import com.android.app.viewcapture.ViewCaptureAwareWindowManager @@ -105,12 +106,19 @@ constructor( @SysUISingleton class SingleDisplayStatusBarWindowControllerStore @Inject -constructor(private val controller: StatusBarWindowController) : StatusBarWindowControllerStore { +constructor( + context: Context, + viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager, + factory: StatusBarWindowControllerImpl.Factory, +) : StatusBarWindowControllerStore { init { StatusBarConnectedDisplays.assertInLegacyMode() } + private val controller: StatusBarWindowController = + factory.create(context, viewCaptureAwareWindowManager) + override val defaultDisplay = controller override fun forDisplay(displayId: Int) = controller diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java index 213581787f73..d0817d736f07 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java @@ -61,7 +61,6 @@ public class TunerActivity extends Activity implements protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setTheme(androidx.appcompat.R.style.Theme_AppCompat_DayNight); getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); requestWindowFeature(Window.FEATURE_NO_TITLE); diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt index de9231856e2c..d87607dec8a1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt +++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt @@ -11,6 +11,7 @@ import android.graphics.drawable.AnimatedStateListDrawable import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable +import android.graphics.drawable.LayerDrawable import android.util.Log import androidx.annotation.Px import com.android.app.tracing.traceSection @@ -22,36 +23,35 @@ class DrawableSize { const val TAG = "SysUiDrawableSize" /** - * Downscales passed Drawable to set maximum width and height. This will only - * be done for Drawables that can be downscaled non-destructively - e.g. animated - * and stateful drawables will no be downscaled. + * Downscales passed Drawable to set maximum width and height. This will only be done for + * Drawables that can be downscaled non-destructively - e.g. animated drawables, stateful + * drawables, and drawables with mixed-type layers will not be downscaled. * - * Downscaling will keep the aspect ratio. - * This method will not touch drawables that already fit into size specification. + * Downscaling will keep the aspect ratio. This method will not touch drawables that already + * fit into size specification. * * @param resources Resources on which to base the density of resized drawable. * @param drawable Drawable to downscale. * @param maxWidth Maximum width of the downscaled drawable. * @param maxHeight Maximum height of the downscaled drawable. - * * @return returns downscaled drawable if it's possible to downscale it or original if it's - * not. + * not. */ @JvmStatic fun downscaleToSize( res: Resources, drawable: Drawable, @Px maxWidth: Int, - @Px maxHeight: Int + @Px maxHeight: Int, ): Drawable { traceSection("DrawableSize#downscaleToSize") { // Bitmap drawables can contain big bitmaps as their content while sneaking it past // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap // size for those. - val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width - ?: drawable.intrinsicWidth - val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height - ?: drawable.intrinsicHeight + val originalWidth = + (drawable as? BitmapDrawable)?.bitmap?.width ?: drawable.intrinsicWidth + val originalHeight = + (drawable as? BitmapDrawable)?.bitmap?.height ?: drawable.intrinsicHeight // Don't touch drawable if we can't resolve sizes for whatever reason. if (originalWidth <= 0 || originalHeight <= 0) { @@ -61,14 +61,18 @@ class DrawableSize { // Do not touch drawables that are already within bounds. if (originalWidth < maxWidth && originalHeight < maxHeight) { if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " + - "to $maxWidth x $maxHeight") + Log.d( + TAG, + "Not resizing $originalWidth x $originalHeight" + + " " + + "to $maxWidth x $maxHeight", + ) } return drawable } - if (!isSimpleBitmap(drawable)) { + if (isComplicatedBitmap(drawable)) { return drawable } @@ -80,19 +84,25 @@ class DrawableSize { val height = (originalHeight * scale).toInt() if (width <= 0 || height <= 0) { - Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " + - "from $originalWidth x $originalHeight to invalid $width x $height.") + Log.w( + TAG, + "Attempted to resize ${drawable.javaClass.simpleName} " + + "from $originalWidth x $originalHeight to invalid $width x $height.", + ) return drawable } if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " + - "from $originalWidth x $originalHeight to $width x $height") + Log.d( + TAG, + "Resizing large drawable (${drawable.javaClass.simpleName}) " + + "from $originalWidth x $originalHeight to $width x $height", + ) } // We want to keep existing config if it's more efficient than 32-bit RGB. - val config = (drawable as? BitmapDrawable)?.bitmap?.config - ?: Bitmap.Config.ARGB_8888 + val config = + (drawable as? BitmapDrawable)?.bitmap?.config ?: Bitmap.Config.ARGB_8888 val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config) val canvas = Canvas(scaledDrawableBitmap) @@ -105,8 +115,8 @@ class DrawableSize { } } - private fun isSimpleBitmap(drawable: Drawable): Boolean { - return !(drawable.isStateful || isAnimated(drawable)) + private fun isComplicatedBitmap(drawable: Drawable): Boolean { + return drawable.isStateful || isAnimated(drawable) || hasComplicatedLayers(drawable) } private fun isAnimated(drawable: Drawable): Boolean { @@ -119,5 +129,30 @@ class DrawableSize { drawable is AnimatedStateListDrawable || drawable is AnimatedVectorDrawable } + + private fun hasComplicatedLayers(drawable: Drawable): Boolean { + if (drawable !is LayerDrawable) { + return false + } + if (drawable.numberOfLayers == 1) { + return false + } + + val firstLayerType = drawable.getDrawable(0).javaClass + for (i in 1..<drawable.numberOfLayers) { + val layer = drawable.getDrawable(i) + if (layer.javaClass != firstLayerType) { + // If different layers have different drawable types, we shouldn't scale it down + // because we may lose the level information if one of the layers is a bitmap + // and another layer is a level-list. See b/244282477. + return true + } + if (isComplicatedBitmap(layer)) { + return true + } + } + + return false + } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt index 2af84c7e46f0..579af73236f3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/GlobalCoroutinesModule.kt @@ -16,10 +16,9 @@ package com.android.systemui.util.kotlin -import com.android.app.tracing.coroutines.createCoroutineTracingContext +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dagger.qualifiers.Tracing import dagger.Module import dagger.Provides import javax.inject.Singleton @@ -27,7 +26,6 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi /** Providers for various application-wide coroutines-related constructs. */ @Module @@ -35,16 +33,15 @@ class GlobalCoroutinesModule { @Provides @Singleton @Application - fun applicationScope( - @Main dispatcherContext: CoroutineContext, - ): CoroutineScope = CoroutineScope(dispatcherContext + createCoroutineTracingContext("ApplicationScope")) + fun applicationScope(@Main dispatcherContext: CoroutineContext): CoroutineScope = + CoroutineScope(dispatcherContext + newTracingContext("ApplicationScope")) @Provides @Singleton @Main @Deprecated( "Use @Main CoroutineContext instead", - ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext") + ReplaceWith("mainCoroutineContext()", "kotlin.coroutines.CoroutineContext"), ) fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt index 4d9aaa6dc6b0..ea8709f7d65c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.util.settings -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.annotation.UserIdInt import android.content.ContentResolver import android.database.ContentObserver @@ -24,6 +23,7 @@ import android.provider.Settings.SettingNotFoundException import androidx.annotation.AnyThread import androidx.annotation.WorkerThread import com.android.app.tracing.TraceUtils.trace +import com.android.systemui.coroutines.newTracingContext import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -94,7 +94,7 @@ interface SettingsProxy { */ @AnyThread fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-A")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-A")).launch { registerContentObserverSync(getUriFor(name), settingsObserver) } @@ -109,9 +109,9 @@ interface SettingsProxy { fun registerContentObserverAsync( name: String, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-B")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-B")).launch { registerContentObserverSync(getUriFor(name), settingsObserver) registered.run() } @@ -144,7 +144,7 @@ interface SettingsProxy { */ @AnyThread fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-C")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-C")).launch { registerContentObserverSync(uri, settingsObserver) } @@ -159,9 +159,9 @@ interface SettingsProxy { fun registerContentObserverAsync( uri: Uri, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-D")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-D")).launch { registerContentObserverSync(uri, settingsObserver) registered.run() } @@ -175,7 +175,7 @@ interface SettingsProxy { fun registerContentObserverSync( name: String, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) /** @@ -204,9 +204,9 @@ interface SettingsProxy { fun registerContentObserverAsync( name: String, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-E")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-E")).launch { registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) } @@ -222,9 +222,9 @@ interface SettingsProxy { name: String, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-F")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-F")).launch { registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) registered.run() } @@ -273,9 +273,9 @@ interface SettingsProxy { fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-G")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-G")).launch { registerContentObserverSync(uri, notifyForDescendants, settingsObserver) } @@ -291,9 +291,9 @@ interface SettingsProxy { uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-H")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-H")).launch { registerContentObserverSync(uri, notifyForDescendants, settingsObserver) registered.run() } @@ -330,7 +330,9 @@ interface SettingsProxy { */ @AnyThread fun unregisterContentObserverAsync(settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("SettingsProxy-I")).launch { unregisterContentObserver(settingsObserver) } + CoroutineScope(backgroundDispatcher + newTracingContext("SettingsProxy-I")).launch { + unregisterContentObserver(settingsObserver) + } /** * Look up a name in the database. diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt index c820c07b61b1..c5deca214e28 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.util.settings -import com.android.app.tracing.coroutines.createCoroutineTracingContext import android.annotation.UserIdInt import android.annotation.WorkerThread import android.content.ContentResolver @@ -24,6 +23,7 @@ import android.net.Uri import android.os.UserHandle import android.provider.Settings.SettingNotFoundException import com.android.app.tracing.TraceUtils.trace +import com.android.systemui.coroutines.newTracingContext import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow @@ -79,7 +79,7 @@ interface UserSettingsProxy : SettingsProxy { } override fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-A")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-A")).launch { registerContentObserverForUserSync(uri, settingsObserver, userId) } @@ -88,7 +88,7 @@ interface UserSettingsProxy : SettingsProxy { override fun registerContentObserverSync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } @@ -111,9 +111,9 @@ interface UserSettingsProxy : SettingsProxy { override fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, - settingsObserver: ContentObserver + settingsObserver: ContentObserver, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-B")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-B")).launch { registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId) } @@ -156,9 +156,9 @@ interface UserSettingsProxy : SettingsProxy { fun registerContentObserverForUserAsync( name: String, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-C")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-C")).launch { registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle) } @@ -197,9 +197,9 @@ interface UserSettingsProxy : SettingsProxy { fun registerContentObserverForUserAsync( uri: Uri, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-D")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-D")).launch { registerContentObserverForUserSync(uri, settingsObserver, userHandle) } @@ -214,9 +214,9 @@ interface UserSettingsProxy : SettingsProxy { uri: Uri, settingsObserver: ContentObserver, userHandle: Int, - @WorkerThread registered: Runnable + @WorkerThread registered: Runnable, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-E")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-E")).launch { registerContentObserverForUserSync(uri, settingsObserver, userHandle) registered.run() } @@ -273,14 +273,14 @@ interface UserSettingsProxy : SettingsProxy { name: String, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) { - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-F")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-F")).launch { registerContentObserverForUserSync( getUriFor(name), notifyForDescendants, settingsObserver, - userHandle + userHandle, ) } } @@ -337,14 +337,14 @@ interface UserSettingsProxy : SettingsProxy { uri: Uri, notifyForDescendants: Boolean, settingsObserver: ContentObserver, - userHandle: Int + userHandle: Int, ) = - CoroutineScope(backgroundDispatcher + createCoroutineTracingContext("UserSettingsProxy-G")).launch { + CoroutineScope(backgroundDispatcher + newTracingContext("UserSettingsProxy-G")).launch { registerContentObserverForUserSync( uri, notifyForDescendants, settingsObserver, - userHandle + userHandle, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepository.kt new file mode 100644 index 000000000000..26fdb9f91413 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepository.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.data.repository + +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +/** Holds current [VolumeDialogStateModel]. */ +@VolumeDialogPlugin +class VolumeDialogStateRepository @Inject constructor() { + + private val mutableState = MutableStateFlow(VolumeDialogStateModel()) + val state: Flow<VolumeDialogStateModel> = mutableState.asStateFlow() + + fun updateState(update: (VolumeDialogStateModel) -> VolumeDialogStateModel) { + mutableState.update(update) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt index 2e26fd6de410..3d125b81cbd2 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt @@ -23,7 +23,6 @@ import com.android.systemui.plugins.VolumeDialogController import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel -import com.android.systemui.volume.dialog.domain.model.VolumeDialogStateModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.ProducerScope @@ -50,7 +49,7 @@ constructor( @Background private val bgHandler: Handler, ) { - @SuppressLint("SharedFlowCreation") // event-but needed + @SuppressLint("SharedFlowCreation") // event-bus needed val event: Flow<VolumeDialogEventModel> = callbackFlow { val producer = VolumeDialogEventModelProducer(this) @@ -79,7 +78,7 @@ constructor( override fun onStateChanged(state: VolumeDialogController.State?) { if (state != null) { - scope.trySend(VolumeDialogEventModel.StateChanged(VolumeDialogStateModel(state))) + scope.trySend(VolumeDialogEventModel.StateChanged(state)) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt index 4a709a44b42f..5c7289baa401 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt @@ -16,20 +16,21 @@ package com.android.systemui.volume.dialog.domain.interactor +import android.util.SparseArray +import androidx.core.util.keyIterator import com.android.systemui.plugins.VolumeDialogController import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope +import com.android.systemui.volume.dialog.data.repository.VolumeDialogStateRepository import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel -import com.android.systemui.volume.dialog.domain.model.VolumeDialogStateModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn /** * Exposes [VolumeDialogController.getState] in the [volumeDialogState]. @@ -42,14 +43,69 @@ class VolumeDialogStateInteractor constructor( volumeDialogCallbacksInteractor: VolumeDialogCallbacksInteractor, private val volumeDialogController: VolumeDialogController, + private val volumeDialogStateRepository: VolumeDialogStateRepository, @VolumeDialogPlugin private val coroutineScope: CoroutineScope, ) { - val volumeDialogState: Flow<VolumeDialogStateModel> = + init { volumeDialogCallbacksInteractor.event + .onEach { event -> + when (event) { + is VolumeDialogEventModel.StateChanged -> { + volumeDialogStateRepository.updateState { oldState -> + event.state.copyIntoModel(oldState) + } + } + is VolumeDialogEventModel.AccessibilityModeChanged -> { + volumeDialogStateRepository.updateState { oldState -> + oldState.copy(shouldShowA11ySlider = event.showA11yStream) + } + } + else -> { + // do nothing + } + } + } .onStart { volumeDialogController.getState() } - .filterIsInstance(VolumeDialogEventModel.StateChanged::class) - .map { it.state } - .stateIn(scope = coroutineScope, started = SharingStarted.Eagerly, initialValue = null) - .filterNotNull() + .launchIn(coroutineScope) + } + + val volumeDialogState: Flow<VolumeDialogStateModel> = volumeDialogStateRepository.state + + /** Returns a copy of [model] filled with the values from [VolumeDialogController.State]. */ + private fun VolumeDialogController.State.copyIntoModel( + model: VolumeDialogStateModel + ): VolumeDialogStateModel { + return model.copy( + streamModels = + states.mapToMap { stream, streamState -> + VolumeDialogStreamModel( + stream = stream, + isActive = stream == activeStream, + legacyState = streamState, + ) + }, + ringerModeInternal = ringerModeInternal, + ringerModeExternal = ringerModeExternal, + zenMode = zenMode, + effectsSuppressor = effectsSuppressor, + effectsSuppressorName = effectsSuppressorName, + activeStream = activeStream, + disallowAlarms = disallowAlarms, + disallowMedia = disallowMedia, + disallowSystem = disallowSystem, + disallowRinger = disallowRinger, + ) + } +} + +private fun <INPUT, OUTPUT> SparseArray<INPUT>.mapToMap( + map: (Int, INPUT) -> OUTPUT +): Map<Int, OUTPUT> { + val resultMap = mutableMapOf<Int, OUTPUT>() + for (key in keyIterator()) { + val mappedValue: OUTPUT = map(key, get(key)!!) + resultMap[key] = mappedValue + } + return resultMap } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt index ca0310ef8588..80e423838251 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.dialog.domain.model import android.media.AudioManager +import com.android.systemui.plugins.VolumeDialogController /** * Models VolumeDialogController callback events. @@ -33,7 +34,7 @@ sealed interface VolumeDialogEventModel { data class DismissRequested(val reason: Int) : VolumeDialogEventModel - data class StateChanged(val state: VolumeDialogStateModel) : VolumeDialogEventModel + data class StateChanged(val state: VolumeDialogController.State) : VolumeDialogEventModel data class LayoutDirectionChanged(val layoutDirection: Int) : VolumeDialogEventModel diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt deleted file mode 100644 index 500cc0bf748c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStateModel.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.volume.dialog.domain.model - -import android.content.ComponentName -import android.util.SparseArray -import androidx.core.util.keyIterator -import com.android.systemui.plugins.VolumeDialogController - -/** Models a state of the Volume Dialog. */ -data class VolumeDialogStateModel( - val states: Map<Int, VolumeDialogStreamModel>, - val ringerModeInternal: Int = 0, - val ringerModeExternal: Int = 0, - val zenMode: Int = 0, - val effectsSuppressor: ComponentName? = null, - val effectsSuppressorName: String? = null, - val activeStream: Int = NO_ACTIVE_STREAM, - val disallowAlarms: Boolean = false, - val disallowMedia: Boolean = false, - val disallowSystem: Boolean = false, - val disallowRinger: Boolean = false, -) { - - constructor( - legacyState: VolumeDialogController.State - ) : this( - states = legacyState.states.mapToMap { VolumeDialogStreamModel(it) }, - ringerModeInternal = legacyState.ringerModeInternal, - ringerModeExternal = legacyState.ringerModeExternal, - zenMode = legacyState.zenMode, - effectsSuppressor = legacyState.effectsSuppressor, - effectsSuppressorName = legacyState.effectsSuppressorName, - activeStream = legacyState.activeStream, - disallowAlarms = legacyState.disallowAlarms, - disallowMedia = legacyState.disallowMedia, - disallowSystem = legacyState.disallowSystem, - disallowRinger = legacyState.disallowRinger, - ) - - companion object { - const val NO_ACTIVE_STREAM: Int = -1 - } -} - -private fun <INPUT, OUTPUT> SparseArray<INPUT>.mapToMap(map: (INPUT) -> OUTPUT): Map<Int, OUTPUT> { - val resultMap = mutableMapOf<Int, OUTPUT>() - for (key in keyIterator()) { - val mappedValue: OUTPUT = map(get(key)!!) - resultMap[key] = mappedValue - } - return resultMap -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt index 2dd0bdab93d1..5e0af634c786 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/settings/domain/VolumeDialogSettingsButtonInteractor.kt @@ -17,7 +17,7 @@ package com.android.systemui.volume.dialog.settings.domain import android.app.ActivityManager -import com.android.app.tracing.coroutines.flow.map +import com.android.app.tracing.coroutines.flow.flowName import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog @@ -30,6 +30,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @VolumeDialogScope @@ -49,6 +50,7 @@ constructor( deviceProvisionedController.isCurrentUserSetup() && model.lockTaskModeState == ActivityManager.LOCK_TASK_MODE_NONE } + .flowName("VDSBI#isVisible") .stateIn(coroutineScope, SharingStarted.Eagerly, false) fun onButtonClicked() { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt new file mode 100644 index 000000000000..1792b9967681 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStateModel.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.shared.model + +import android.content.ComponentName + +/** Models a state of the Volume Dialog. */ +data class VolumeDialogStateModel( + val shouldShowA11ySlider: Boolean = false, + val streamModels: Map<Int, VolumeDialogStreamModel> = mapOf(), + val ringerModeInternal: Int = 0, + val ringerModeExternal: Int = 0, + val zenMode: Int = 0, + val effectsSuppressor: ComponentName? = null, + val effectsSuppressorName: String? = null, + val activeStream: Int = NO_ACTIVE_STREAM, + val disallowAlarms: Boolean = false, + val disallowMedia: Boolean = false, + val disallowSystem: Boolean = false, + val disallowRinger: Boolean = false, +) { + + companion object { + const val NO_ACTIVE_STREAM: Int = -1 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt index 26c96eabc65f..be3cd97edf10 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogStreamModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/model/VolumeDialogStreamModel.kt @@ -14,14 +14,16 @@ * limitations under the License. */ -package com.android.systemui.volume.dialog.domain.model +package com.android.systemui.volume.dialog.shared.model import androidx.annotation.StringRes import com.android.systemui.plugins.VolumeDialogController /** Models a state of an audio stream of the Volume Dialog. */ data class VolumeDialogStreamModel( + val stream: Int, val isDynamic: Boolean = false, + val isActive: Boolean, val level: Int = 0, val levelMin: Int = 0, val levelMax: Int = 0, @@ -32,8 +34,12 @@ data class VolumeDialogStreamModel( val routedToBluetooth: Boolean = false, ) { constructor( - legacyState: VolumeDialogController.StreamState + stream: Int, + isActive: Boolean, + legacyState: VolumeDialogController.StreamState, ) : this( + stream = stream, + isActive = isActive, isDynamic = legacyState.dynamic, level = legacyState.level, levelMin = legacyState.levelMin, diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt index 81507ba7dc60..f78a8dcabc1c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt @@ -19,7 +19,7 @@ package com.android.systemui.volume.dialog.sliders.domain.interactor import com.android.systemui.plugins.VolumeDialogController import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor -import com.android.systemui.volume.dialog.domain.model.VolumeDialogStreamModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -38,7 +38,7 @@ constructor( val slider: Flow<VolumeDialogStreamModel> = volumeDialogStateInteractor.volumeDialogState.mapNotNull { - it.states[sliderType.audioStream] + it.streamModels[sliderType.audioStream] } fun setStreamVolume(userLevel: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt index 325e4c9514cf..7af4258930cb 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt @@ -16,45 +16,127 @@ package com.android.systemui.volume.dialog.sliders.domain.interactor +import android.content.pm.PackageManager +import android.media.AudioManager +import android.media.AudioSystem +import com.android.settingslib.flags.Flags import com.android.systemui.volume.VolumeDialogControllerImpl +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSlidersModel import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +private const val DEFAULT_STREAM = AudioManager.STREAM_MUSIC /** Provides a state for the Sliders section of the Volume Dialog. */ @VolumeDialogScope class VolumeDialogSlidersInteractor @Inject -constructor(volumeDialogStateInteractor: VolumeDialogStateInteractor) { +constructor( + volumeDialogStateInteractor: VolumeDialogStateInteractor, + private val packageManager: PackageManager, + @VolumeDialog private val coroutineScope: CoroutineScope, +) { + private val streamsSorter = StreamsSorter() val sliders: Flow<VolumeDialogSlidersModel> = - volumeDialogStateInteractor.volumeDialogState.map { - val sliderTypes: List<VolumeDialogSliderType> = - it.states.keys.sortedWith(StreamsSorter).map { audioStream -> - when { - audioStream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST -> - VolumeDialogSliderType.AudioSharingStream(audioStream) - audioStream >= - VolumeDialogControllerImpl.DYNAMIC_STREAM_REMOTE_START_INDEX -> - VolumeDialogSliderType.RemoteMediaStream(audioStream) - else -> VolumeDialogSliderType.Stream(audioStream) - } - } - VolumeDialogSlidersModel( - slider = sliderTypes.first(), - floatingSliders = sliderTypes.drop(1), - ) + volumeDialogStateInteractor.volumeDialogState + .filter { it.streamModels.isNotEmpty() } + .map { stateModel -> + stateModel.streamModels.values + .filter { streamModel -> shouldShowSliders(stateModel, streamModel) } + .sortedWith(streamsSorter) + } + .map { models -> + val sliderTypes: List<VolumeDialogSliderType> = + models.map { model -> model.toType() } + VolumeDialogSlidersModel( + slider = sliderTypes.first(), + floatingSliders = sliderTypes.drop(1), + ) + } + .stateIn(coroutineScope, SharingStarted.Eagerly, null) + .filterNotNull() + + private fun shouldShowSliders( + stateModel: VolumeDialogStateModel, + streamModel: VolumeDialogStreamModel, + ): Boolean { + if (streamModel.isActive) { + return true } - private object StreamsSorter : Comparator<Int> { + if (!packageManager.isTv()) { + if (streamModel.stream == AudioSystem.STREAM_ACCESSIBILITY) { + return stateModel.shouldShowA11ySlider + } - // TODO(b/369992924) order the streams - override fun compare(lhs: Int, rhs: Int): Int { - return lhs - rhs + // Always show the stream for audio sharing if it exists. + if ( + Flags.volumeDialogAudioSharingFix() && + streamModel.stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST + ) { + return true + } + + return streamModel.stream == DEFAULT_STREAM || streamModel.isDynamic + } + + return false + } + + private fun VolumeDialogStreamModel.toType(): VolumeDialogSliderType { + return when { + stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST -> + VolumeDialogSliderType.AudioSharingStream(stream) + stream >= VolumeDialogControllerImpl.DYNAMIC_STREAM_REMOTE_START_INDEX -> + VolumeDialogSliderType.RemoteMediaStream(stream) + else -> VolumeDialogSliderType.Stream(stream) + } + } + + private class StreamsSorter : Comparator<VolumeDialogStreamModel> { + + /** + * This list reflects the order of the sorted collection. Elements that satisfy predicates + * at the beginning of this list will be earlier in the sorted collection. + */ + private val priorityPredicates: List<(VolumeDialogStreamModel) -> Boolean> = + listOf( + { it.isActive }, + { it.stream == AudioManager.STREAM_MUSIC }, + { it.stream == AudioManager.STREAM_ACCESSIBILITY }, + { it.stream == AudioManager.STREAM_RING }, + { it.stream == AudioManager.STREAM_NOTIFICATION }, + { it.stream == AudioManager.STREAM_VOICE_CALL }, + { it.stream == AudioManager.STREAM_SYSTEM }, + { it.isDynamic }, + ) + + override fun compare(lhs: VolumeDialogStreamModel, rhs: VolumeDialogStreamModel): Int { + return lhs.getPriority() - rhs.getPriority() + } + + private fun VolumeDialogStreamModel.getPriority(): Int { + val index = priorityPredicates.indexOfFirst { it(this) } + return if (index >= 0) { + index + } else { + stream + } } } } + +private fun PackageManager.isTv(): Boolean = hasSystemFeature(PackageManager.FEATURE_LEANBACK) diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt index 18a26891e904..605b54a9321b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderType.kt @@ -24,9 +24,9 @@ sealed interface VolumeDialogSliderType { // into separated interactors. val audioStream: Int - class Stream(override val audioStream: Int) : VolumeDialogSliderType + data class Stream(override val audioStream: Int) : VolumeDialogSliderType - class RemoteMediaStream(override val audioStream: Int) : VolumeDialogSliderType + data class RemoteMediaStream(override val audioStream: Int) : VolumeDialogSliderType - class AudioSharingStream(override val audioStream: Int) : VolumeDialogSliderType + data class AudioSharingStream(override val audioStream: Int) : VolumeDialogSliderType } 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 27b8f2f5adb7..7ee722d97bbc 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 @@ -16,7 +16,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel -import com.android.systemui.volume.dialog.domain.model.VolumeDialogStreamModel +import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor import dagger.assisted.Assisted import dagger.assisted.AssistedFactory diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml new file mode 100644 index 000000000000..d9ea0b91abdf --- /dev/null +++ b/packages/SystemUI/tests/res/drawable/layer_drawable_all_same_type.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <item android:drawable="@drawable/ic_brightness"/> + <item android:drawable="@drawable/ic_brightness"/> +</layer-list> diff --git a/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml new file mode 100644 index 000000000000..796de8f15f47 --- /dev/null +++ b/packages/SystemUI/tests/res/drawable/layer_drawable_different_types.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android" > + <!-- dessert_flan is a PNG while ic_brightness is a level-list. --> + <item android:drawable="@drawable/dessert_flan"/> + <item android:drawable="@drawable/ic_brightness"/> +</layer-list> diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index e2a6a5508992..4baca713e19f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -16,21 +16,14 @@ package com.android.systemui.biometrics -import android.graphics.Rect import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_BP import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_KEYGUARD -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_OTHER -import android.hardware.biometrics.BiometricRequestConstants.REASON_AUTH_SETTINGS -import android.hardware.biometrics.BiometricRequestConstants.REASON_ENROLL_ENROLLING import android.hardware.biometrics.BiometricRequestConstants.RequestReason import android.hardware.fingerprint.IUdfpsOverlayControllerCallback import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater import android.view.MotionEvent -import android.view.Surface -import android.view.Surface.Rotation import android.view.View -import android.view.ViewGroup import android.view.WindowManager import android.view.accessibility.AccessibilityManager import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -38,7 +31,6 @@ import androidx.test.filters.SmallTest import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.ViewCaptureAwareWindowManager import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor @@ -61,9 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessState -import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor -import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.SystemUIDialogManager import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController @@ -86,7 +76,6 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever @@ -118,7 +107,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var dialogManager: SystemUIDialogManager @Mock private lateinit var dumpManager: DumpManager - @Mock private lateinit var transitionController: LockscreenShadeTransitionController @Mock private lateinit var configurationController: ConfigurationController @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock @@ -126,8 +114,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider @Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback @Mock private lateinit var udfpsController: UdfpsController - @Mock private lateinit var udfpsView: UdfpsView - @Mock private lateinit var mUdfpsKeyguardViewLegacy: UdfpsKeyguardViewLegacy @Mock private lateinit var mActivityTransitionAnimator: ActivityTransitionAnimator @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @@ -147,7 +133,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { private lateinit var powerInteractor: PowerInteractor private lateinit var testScope: TestScope - private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } + private val onTouch = { _: View, _: MotionEvent -> true } private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams() private lateinit var controllerOverlay: UdfpsControllerOverlay @@ -158,53 +144,37 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { powerInteractor = kosmos.powerInteractor keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor - whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView) - whenever(inflater.inflate(R.layout.udfps_bp_view, null)) - .thenReturn(mock(UdfpsBpView::class.java)) - whenever(inflater.inflate(R.layout.udfps_keyguard_view_legacy, null)) - .thenReturn(mUdfpsKeyguardViewLegacy) - whenever(inflater.inflate(R.layout.udfps_fpm_empty_view, null)) - .thenReturn(mock(UdfpsFpmEmptyView::class.java)) } private suspend fun withReasonSuspend( @RequestReason reason: Int, isDebuggable: Boolean = false, - enableDeviceEntryUdfpsRefactor: Boolean = false, block: suspend () -> Unit, ) { - withReason( - reason, - isDebuggable, - enableDeviceEntryUdfpsRefactor, - ) + withReason(reason, isDebuggable) block() } private fun withReason( @RequestReason reason: Int, isDebuggable: Boolean = false, - enableDeviceEntryUdfpsRefactor: Boolean = false, block: () -> Unit = {}, ) { - if (enableDeviceEntryUdfpsRefactor) { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } else { - mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - } controllerOverlay = UdfpsControllerOverlay( context, inflater, - ViewCaptureAwareWindowManager(windowManager, lazyViewCapture, - isViewCaptureEnabled = false), + ViewCaptureAwareWindowManager( + windowManager, + lazyViewCapture, + isViewCaptureEnabled = false, + ), accessibilityManager, statusBarStateController, statusBarKeyguardViewManager, keyguardUpdateMonitor, dialogManager, dumpManager, - transitionController, configurationController, keyguardStateController, unlockedScreenOffAnimationController, @@ -230,117 +200,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { block() } - @Test fun showUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { showUdfpsOverlay() } - - @Test - fun showUdfpsOverlay_keyguard() = - withReason(REASON_AUTH_KEYGUARD) { - showUdfpsOverlay() - verify(mUdfpsKeyguardViewLegacy).updateSensorLocation(eq(overlayParams.sensorBounds)) - } - - @Test fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() } - - private fun withRotation(@Rotation rotation: Int, block: () -> Unit) { - // Sensor that's in the top left corner of the display in natural orientation. - val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT) - val overlayBounds = Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT) - overlayParams = - UdfpsOverlayParams( - sensorBounds, - overlayBounds, - DISPLAY_WIDTH, - DISPLAY_HEIGHT, - scaleFactor = 1f, - rotation - ) - block() - } - - @Test - fun showUdfpsOverlay_withRotation0() = - withRotation(Surface.ROTATION_0) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // ROTATION_0 is the native orientation. Sensor should stay in the top left corner. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) - assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) - } - } - - @Test - fun showUdfpsOverlay_withRotation180() = - withRotation(Surface.ROTATION_180) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // ROTATION_180 is not supported. Sensor should stay in the top left corner. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_WIDTH) - assertThat(lp.height).isEqualTo(DISPLAY_HEIGHT) - } - } - - @Test - fun showUdfpsOverlay_withRotation90() = - withRotation(Surface.ROTATION_90) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Sensor should be in the bottom left corner in ROTATION_90. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) - assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) - } - } - - @Test - fun showUdfpsOverlay_withRotation270() = - withRotation(Surface.ROTATION_270) { - withReason(REASON_AUTH_BP) { - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Sensor should be in the top right corner in ROTATION_270. - val lp = layoutParamsCaptor.value - assertThat(lp.x).isEqualTo(0) - assertThat(lp.y).isEqualTo(0) - assertThat(lp.width).isEqualTo(DISPLAY_HEIGHT) - assertThat(lp.height).isEqualTo(DISPLAY_WIDTH) - } - } - - @Test - fun showUdfpsOverlay_awake() = - testScope.runTest { - withReason(REASON_AUTH_KEYGUARD) { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - verify(windowManager).addView(any(), any()) - } - } - @Test fun showUdfpsOverlay_whileGoingToSleep() = testScope.runTest { @@ -412,91 +271,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun showUdfpsOverlay_afterFinishedTransitioningToAOD() = - testScope.runTest { - withReasonSuspend(REASON_AUTH_KEYGUARD) { - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.OFF, - to = KeyguardState.GONE, - testScope = this, - ) - powerRepository.updateWakefulness( - rawState = WakefulnessState.STARTING_TO_SLEEP, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - - // WHEN a request comes to show the view - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - - // THEN the view does not get added immediately - verify(windowManager, never()).addView(any(), any()) - - // WHEN the device finishes transitioning to AOD - keyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.GONE, - to = KeyguardState.AOD, - testScope = this, - ) - runCurrent() - - // THEN the view gets added - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - } - } - - private fun showUdfpsOverlay() { - val didShow = controllerOverlay.show(udfpsController, overlayParams) - - verify(windowManager).addView(eq(controllerOverlay.getTouchOverlay()), any()) - verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode)) - verify(udfpsView).animationViewController = any() - verify(udfpsView).addView(any()) - - assertThat(didShow).isTrue() - assertThat(controllerOverlay.isShowing).isTrue() - assertThat(controllerOverlay.isHiding).isFalse() - assertThat(controllerOverlay.getTouchOverlay()).isNotNull() - } - - @Test fun hideUdfpsOverlay_bp() = withReason(REASON_AUTH_BP) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_keyguard() = withReason(REASON_AUTH_KEYGUARD) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_settings() = withReason(REASON_AUTH_SETTINGS) { hideUdfpsOverlay() } - - @Test fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() } - - private fun hideUdfpsOverlay() { - val didShow = controllerOverlay.show(udfpsController, overlayParams) - val view = controllerOverlay.getTouchOverlay() - view?.let { whenever(view.parent).thenReturn(mock(ViewGroup::class.java)) } - val didHide = controllerOverlay.hide() - - verify(windowManager).removeView(eq(view)) - - assertThat(didShow).isTrue() - assertThat(didHide).isTrue() - assertThat(controllerOverlay.getTouchOverlay()).isNull() - assertThat(controllerOverlay.animationViewController).isNull() - assertThat(controllerOverlay.isShowing).isFalse() - assertThat(controllerOverlay.isHiding).isTrue() - } - - @Test fun canNotHide() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.hide()).isFalse() } @Test - fun canNotReshow() = - withReason(REASON_AUTH_BP) { - assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue() - assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse() - } - - @Test fun cancels() = withReason(REASON_AUTH_BP) { controllerOverlay.cancel() @@ -504,16 +281,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun unconfigureDisplayOnHide() = - withReason(REASON_AUTH_BP) { - whenever(udfpsView.isDisplayConfigured).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - controllerOverlay.hide() - verify(udfpsView).unconfigureDisplay() - } - - @Test fun matchesRequestIds() = withReason(REASON_AUTH_BP) { assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue() @@ -521,29 +288,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { } @Test - fun smallOverlayOnEnrollmentWithA11y() = - withRotation(Surface.ROTATION_0) { - withReason(REASON_ENROLL_ENROLLING) { - // When a11y enabled during enrollment - whenever(accessibilityManager.isTouchExplorationEnabled).thenReturn(true) - - controllerOverlay.show(udfpsController, overlayParams) - verify(windowManager) - .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture()) - - // Layout params should use sensor bounds - val lp = layoutParamsCaptor.value - assertThat(lp.width).isEqualTo(overlayParams.sensorBounds.width()) - assertThat(lp.height).isEqualTo(overlayParams.sensorBounds.height()) - } - } - - @Test fun addViewPending_layoutIsNotUpdated() = testScope.runTest { withReasonSuspend(REASON_AUTH_KEYGUARD) { - mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - // GIVEN going to sleep keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.OFF, @@ -574,26 +321,4 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.hide() } } - - @Test - fun updateOverlayParams_viewLayoutUpdated() = - testScope.runTest { - withReasonSuspend(REASON_AUTH_KEYGUARD) { - powerRepository.updateWakefulness( - rawState = WakefulnessState.AWAKE, - lastWakeReason = WakeSleepReason.POWER_BUTTON, - lastSleepReason = WakeSleepReason.OTHER, - ) - runCurrent() - controllerOverlay.show(udfpsController, overlayParams) - runCurrent() - verify(windowManager).addView(any(), any()) - - // WHEN updateOverlayParams gets called - controllerOverlay.updateOverlayParams(overlayParams) - - // THEN the view layout is updated - verify(windowManager).updateViewLayout(any(), any()) - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index ae635b8cbfd4..df50f765349c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -77,6 +77,7 @@ import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; import com.android.systemui.util.RingerModeLiveData; @@ -125,6 +126,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private LightBarController mLightBarController; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private StatusBarWindowController mStatusBarWindowController; + @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore; @Mock private IWindowManager mWindowManager; @Mock private Executor mBackgroundExecutor; @Mock private UiEventLogger mUiEventLogger; @@ -155,7 +157,8 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { when(mUserContextProvider.getUserContext()).thenReturn(mContext); when(mResources.getConfiguration()).thenReturn( getContext().getResources().getConfiguration()); - + when(mStatusBarWindowControllerStore.getDefaultDisplay()) + .thenReturn(mStatusBarWindowController); mGlobalSettings = new FakeGlobalSettings(); mSecureSettings = new FakeSettings(); mInteractor = mKosmos.getGlobalActionsInteractor(); @@ -184,7 +187,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mStatusBarService, mLightBarController, mNotificationShadeWindowController, - mStatusBarWindowController, + mStatusBarWindowControllerStore, mWindowManager, mBackgroundExecutor, mUiEventLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt index 96a0aadacbc3..ecc62e908a4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt @@ -298,7 +298,7 @@ class ClockSectionTest : SysuiTestCase() { underTest.applyDefaultConstraints(cs) val referencedIds = cs.getReferencedIds(R.id.weather_clock_date_and_icons_barrier_bottom) - referencedIds.contentEquals(intArrayOf(R.id.lockscreen_clock_view)) + referencedIds.contentEquals(intArrayOf(customR.id.lockscreen_clock_view)) } @Test @@ -323,7 +323,7 @@ class ClockSectionTest : SysuiTestCase() { } private fun assertLargeClockTop(cs: ConstraintSet, expectedLargeClockTopMargin: Int) { - val largeClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view_large) + val largeClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view_large) assertThat(largeClockConstraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID) assertThat(largeClockConstraint.layout.topMargin).isEqualTo(expectedLargeClockTopMargin) } @@ -332,7 +332,7 @@ class ClockSectionTest : SysuiTestCase() { val smallClockGuidelineConstraint = cs.getConstraint(R.id.small_clock_guideline_top) assertThat(smallClockGuidelineConstraint.layout.topToTop).isEqualTo(-1) - val smallClockConstraint = cs.getConstraint(R.id.lockscreen_clock_view) + val smallClockConstraint = cs.getConstraint(customR.id.lockscreen_clock_view) assertThat(smallClockConstraint.layout.topToBottom) .isEqualTo(R.id.small_clock_guideline_top) assertThat(smallClockConstraint.layout.topMargin).isEqualTo(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt index bfb8a57e6271..cea51a89a378 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt @@ -18,13 +18,11 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.graphics.Point -import android.platform.test.annotations.DisableFlags import android.view.WindowManager import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.keyguard.LegacyLockIconViewController import com.android.systemui.Flags as AConfigFlags import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController @@ -60,7 +58,6 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager @Mock private lateinit var notificationPanelView: NotificationPanelView private lateinit var featureFlags: FakeFeatureFlags - @Mock private lateinit var lockIconViewController: LegacyLockIconViewController @Mock private lateinit var falsingManager: FalsingManager @Mock private lateinit var deviceEntryIconViewModel: DeviceEntryIconViewModel private lateinit var underTest: DefaultDeviceEntrySection @@ -81,7 +78,6 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { context, notificationPanelView, featureFlags, - { lockIconViewController }, { deviceEntryIconViewModel }, { mock(DeviceEntryForegroundViewModel::class.java) }, { mock(DeviceEntryBackgroundViewModel::class.java) }, @@ -102,37 +98,13 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { @Test fun addViewsConditionally_migrateAndRefactorFlagsOn() { mSetFlagsRule.enableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) val constraintLayout = ConstraintLayout(context, null) underTest.addViews(constraintLayout) assertThat(constraintLayout.childCount).isGreaterThan(0) } @Test - @DisableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) - fun addViewsConditionally_migrateFlagOff() { - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR) - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - val constraintLayout = ConstraintLayout(context, null) - underTest.addViews(constraintLayout) - assertThat(constraintLayout.childCount).isEqualTo(0) - } - - @Test - fun applyConstraints_udfps_refactor_off() { - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - val cs = ConstraintSet() - underTest.applyConstraints(cs) - - val constraint = cs.getConstraint(R.id.lock_icon_view) - - assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID) - assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID) - } - - @Test - fun applyConstraints_udfps_refactor_on() { - mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) + fun applyConstraints() { whenever(deviceEntryIconViewModel.isUdfpsSupported).thenReturn(MutableStateFlow(false)) val cs = ConstraintSet() underTest.applyConstraints(cs) @@ -144,24 +116,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { } @Test - fun testCenterIcon_udfps_refactor_off() { - mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - val cs = ConstraintSet() - underTest.centerIcon(Point(5, 6), 1F, cs) - - val constraint = cs.getConstraint(R.id.lock_icon_view) - - assertThat(constraint.layout.mWidth).isEqualTo(2) - assertThat(constraint.layout.mHeight).isEqualTo(2) - assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID) - assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID) - assertThat(constraint.layout.topMargin).isEqualTo(5) - assertThat(constraint.layout.startMargin).isEqualTo(4) - } - - @Test - fun testCenterIcon_udfps_refactor_on() { - mSetFlagsRule.enableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) + fun testCenterIcon() { val cs = ConstraintSet() underTest.centerIcon(Point(5, 6), 1F, cs) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt new file mode 100644 index 000000000000..0d1d37af7e5b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarInitializerStoreTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import android.platform.test.annotations.EnableFlags +import android.view.Display +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.display.data.repository.displayRepository +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.unconfinedTestDispatcher +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) +class MultiDisplayStatusBarInitializerStoreTest : SysuiTestCase() { + + private val kosmos = + testKosmos().also { + // Using unconfinedTestDispatcher to avoid having to call `runCurrent` in the tests. + it.testDispatcher = it.unconfinedTestDispatcher + } + private val testScope = kosmos.testScope + private val fakeDisplayRepository = kosmos.displayRepository + private val store = kosmos.multiDisplayStatusBarInitializerStore + + @Before + fun start() { + store.start() + } + + @Before + fun addDisplays() = runBlocking { + fakeDisplayRepository.addDisplay(DEFAULT_DISPLAY_ID) + fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID) + } + + @Test + fun forDisplay_defaultDisplay_multipleCalls_returnsSameInstance() = + testScope.runTest { + val controller = store.defaultDisplay + + assertThat(store.defaultDisplay).isSameInstanceAs(controller) + } + + @Test + fun forDisplay_nonDefaultDisplay_multipleCalls_returnsSameInstance() = + testScope.runTest { + val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID) + + assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isSameInstanceAs(controller) + } + + @Test + fun forDisplay_nonDefaultDisplay_afterDisplayRemoved_returnsNewInstance() = + testScope.runTest { + val controller = store.forDisplay(NON_DEFAULT_DISPLAY_ID) + + fakeDisplayRepository.removeDisplay(NON_DEFAULT_DISPLAY_ID) + fakeDisplayRepository.addDisplay(NON_DEFAULT_DISPLAY_ID) + + assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(controller) + } + + @Test(expected = IllegalArgumentException::class) + fun forDisplay_nonExistingDisplayId_throws() = + testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) } + + companion object { + private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY + private const val NON_DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1 + private const val NON_EXISTING_DISPLAY_ID = Display.DEFAULT_DISPLAY + 2 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt index 376873d19624..5d8a8fd03bc0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.statusbar.BatteryStatusChip import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock @@ -63,6 +64,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { @Mock private lateinit var systemEventCoordinator: SystemEventCoordinator @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var statusBarWindowControllerStore: StatusBarWindowControllerStore @Mock private lateinit var statusBarContentInsetProvider: StatusBarContentInsetsProvider @@ -82,12 +84,14 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { fun setup() { MockitoAnnotations.initMocks(this) + whenever(statusBarWindowControllerStore.defaultDisplay) + .thenReturn(statusBarWindowController) systemClock = FakeSystemClock() chipAnimationController = SystemEventChipAnimationController( mContext, - statusBarWindowController, - statusBarContentInsetProvider + statusBarWindowControllerStore, + statusBarContentInsetProvider, ) // StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values. @@ -660,7 +664,7 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { SystemStatusAnimationSchedulerImpl( systemEventCoordinator, chipAnimationController, - statusBarWindowController, + statusBarWindowControllerStore, dumpManager, systemClock, CoroutineScope(StandardTestDispatcher(testScope.testScheduler)), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt deleted file mode 100644 index ac7388281a15..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt +++ /dev/null @@ -1,104 +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 - -import android.platform.test.annotations.DisableFlags -import android.provider.DeviceConfig -import android.provider.Settings -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.dx.mockito.inline.extended.ExtendedMockito -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING -import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.notification.shared.NotificationMinimalism -import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection -import com.android.systemui.util.DeviceConfigProxyFake -import com.android.systemui.util.Utils -import com.android.systemui.util.mockito.any -import org.junit.After -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.MockitoSession -import org.mockito.kotlin.whenever -import org.mockito.quality.Strictness - -@RunWith(AndroidJUnit4::class) -@SmallTest -// this class has no testable logic with either of these flags enabled -@DisableFlags(PriorityPeopleSection.FLAG_NAME, NotificationMinimalism.FLAG_NAME) -class NotificationSectionsFeatureManagerTest : SysuiTestCase() { - lateinit var manager: NotificationSectionsFeatureManager - private val proxyFake = DeviceConfigProxyFake() - private lateinit var staticMockSession: MockitoSession - - @Before - fun setup() { - manager = NotificationSectionsFeatureManager(proxyFake, mContext) - manager.clearCache() - staticMockSession = - ExtendedMockito.mockitoSession() - .mockStatic(Utils::class.java) - .strictness(Strictness.LENIENT) - .startMocking() - whenever(Utils.useQsMediaPlayer(any())).thenReturn(false) - Settings.Global.putInt( - context.getContentResolver(), - Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, - 0 - ) - } - - @After - fun teardown() { - staticMockSession.finishMocking() - } - - @Test - fun testPeopleFilteringOff_newInterruptionModelOn() { - proxyFake.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - NOTIFICATIONS_USE_PEOPLE_FILTERING, - "false", - false - ) - - assertFalse("People filtering should be disabled", manager.isFilteringEnabled()) - assertTrue( - "Expecting 2 buckets when people filtering is disabled", - manager.getNumberOfBuckets() == 2 - ) - } - - @Test - fun testPeopleFilteringOn_newInterruptionModelOn() { - proxyFake.setProperty( - DeviceConfig.NAMESPACE_SYSTEMUI, - NOTIFICATIONS_USE_PEOPLE_FILTERING, - "true", - false - ) - - assertTrue("People filtering should be enabled", manager.isFilteringEnabled()) - assertTrue( - "Expecting 5 buckets when people filtering is enabled", - manager.getNumberOfBuckets() == 5 - ) - } -} 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 15ea811287b8..44d81a7abfe5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED; import static android.provider.Settings.Global.HEADS_UP_ON; -import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR; import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE; import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION; import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT; @@ -170,6 +169,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.core.StatusBarInitializerImpl; import com.android.systemui.statusbar.core.StatusBarOrchestrator; +import com.android.systemui.statusbar.core.StatusBarSimpleFragment; import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -194,6 +194,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserInfoControllerImpl; import com.android.systemui.statusbar.window.StatusBarWindowController; +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.util.FakeEventLog; import com.android.systemui.util.WallpaperController; @@ -292,6 +293,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private AutoHideController mAutoHideController; @Mock private StatusBarWindowController mStatusBarWindowController; + @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore; @Mock private Provider<CollapsedStatusBarFragment> mCollapsedStatusBarFragmentProvider; @Mock private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private Bubbles mBubbles; @@ -383,6 +385,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { when(mBubbles.canShowBubbleNotification()).thenReturn(true); + when(mStatusBarWindowControllerStore.getDefaultDisplay()) + .thenReturn(mStatusBarWindowController); + mVisualInterruptionDecisionProvider = VisualInterruptionDecisionProviderTestUtil.INSTANCE.createProviderByFlag( mAmbientDisplayConfiguration, @@ -465,7 +470,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager, - mStatusBarWindowController, + mStatusBarWindowControllerStore, mDeviceProvisionedController, mNotificationShadeWindowController, 0, @@ -507,10 +512,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mLightBarController, mAutoHideController, new StatusBarInitializerImpl( - mStatusBarWindowController, + mContext.getDisplayId(), + mStatusBarWindowControllerStore, mCollapsedStatusBarFragmentProvider, emptySet()), - mStatusBarWindowController, + mStatusBarWindowControllerStore, mStatusBarWindowStateController, new FakeStatusBarModeRepository(), mKeyguardUpdateMonitor, @@ -852,34 +858,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() { - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true); - - mCentralSurfaces.updateScrimController(); - - verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE)); - verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway(); - } - - @Test - @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() { - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN device occluded and panel is NOT expanded - mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE - when(mKeyguardStateController.isOccluded()).thenReturn(true); - when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false); - - mCentralSurfaces.updateScrimController(); - - verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED)); - } - - @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() { when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); @@ -895,7 +873,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() { when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); @@ -911,21 +888,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR) - public void testOccludingQSExpanded_transitionToAuthScrimmedShade() { - when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); - - // GIVEN device occluded and qs IS expanded - mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE - when(mKeyguardStateController.isOccluded()).thenReturn(true); - when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true); - - mCentralSurfaces.updateScrimController(); - - verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE)); - } - - @Test public void testEnteringGlanceableHub_updatesScrim() { // Transition to the glanceable hub. mKosmos.getCommunalRepository() @@ -1149,6 +1111,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test + @DisableFlags(StatusBarSimpleFragment.FLAG_NAME) public void bubbleBarVisibility() { createCentralSurfaces(); mCentralSurfaces.onStatusBarWindowStateChanged(WINDOW_STATE_HIDDEN); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt index 68df7488ee10..ee79ca0df9d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -57,10 +58,15 @@ class PhoneStatusBarViewTest : SysuiTestCase() { get() = view.requireViewById(R.id.system_icons) private val windowController = mock<StatusBarWindowController>() + private val windowControllerStore = mock<StatusBarWindowControllerStore>() @Before fun setUp() { - mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController) + whenever(windowControllerStore.defaultDisplay).thenReturn(windowController) + mDependency.injectTestDependency( + StatusBarWindowControllerStore::class.java, + windowControllerStore, + ) context.ensureTestableResources() view = spy(createStatusBarView()) whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index e804b33db1f7..eecf36e9343b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -309,8 +309,6 @@ public class ScrimControllerTest extends SysuiTestCase { // Attach behind scrim so flows that are collecting on it start running. ViewUtils.attachView(mScrimBehind); - mScrimController.setHasBackdrop(false); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); @@ -483,47 +481,6 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() { - mScrimController.setHasBackdrop(true); - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true - )); - } - - @Test - public void setHasBackdrop_withAodWallpaperAndAlbumArt() { - mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); - mTestScope.getTestScheduler().runCurrent(); - - mScrimController.legacyTransitionTo(ScrimState.AOD); - finishAnimationsImmediately(); - mScrimController.setHasBackdrop(true); - finishAnimationsImmediately(); - - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); - - assertScrimTinted(Map.of( - mScrimInFront, true, - mScrimBehind, true - )); - } - - @Test public void transitionToAod_withFrontAlphaUpdates() { // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state. mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); @@ -1356,7 +1313,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); - mScrimController.setHasBackdrop(false); mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); mTestScope.getTestScheduler().runCurrent(); mScrimController.legacyTransitionTo(ScrimState.KEYGUARD); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index e57e8d108529..15ef917343ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.systemui.Flags.FLAG_STATUS_BAR_RON_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; @@ -61,6 +60,7 @@ import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -633,7 +633,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, - FLAG_STATUS_BAR_RON_CHIPS, + StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() { resumeAndGetFragment(); @@ -660,8 +660,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasSecondaryOngoingActivity_butRonsFlagOff_secondaryChipHidden() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() { resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -673,7 +673,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() { resumeAndGetFragment(); @@ -689,8 +689,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -705,9 +705,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_ronsFlagOn() { + public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -724,8 +724,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivityButAlsoHun_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -740,9 +740,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivitiesButAlsoHun_chipsHidden_ronsFlagOn() { + public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( @@ -759,8 +759,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void primaryOngoingActivityEnded_chipHidden_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() { resumeAndGetFragment(); // Ongoing activity started @@ -781,9 +781,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void primaryOngoingActivityEnded_chipHidden_ronsFlagOn() { + public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() { resumeAndGetFragment(); // Ongoing activity started @@ -804,7 +804,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) public void secondaryOngoingActivityEnded_chipHidden() { resumeAndGetFragment(); @@ -828,8 +828,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -846,9 +846,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void hasOngoingActivity_hidesNotifsWithoutAnimation_ronsFlagOn() { + public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating fragment.enableAnimationsForTesting(); @@ -866,8 +866,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({FLAG_STATUS_BAR_RON_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOff() { + @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback @@ -898,9 +898,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_RON_CHIPS}) + @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - public void screenSharingChipsEnabled_ignoresOngoingCallController_ronsFlagOn() { + public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // WHEN there *is* an ongoing call via old callback 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 9cfb0bb3900b..1ceb20adeebd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -46,6 +46,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; @@ -197,6 +198,8 @@ import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; +import kotlin.Lazy; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -215,7 +218,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.Executor; -import kotlin.Lazy; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -2470,6 +2472,52 @@ public class BubblesTest extends SysuiTestCase { verify(stackView, never()).showOverflow(anyBoolean()); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_addBubble() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_POSTED)); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_updateBubble() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + // Mark the notification as updated + NotificationEntryHelper.modifyRanking(mRow).setTextChanged(true).build(); + mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true); + + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_UPDATED)); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragBubbleToDismiss() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L); + + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE)); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); @@ -2655,6 +2703,10 @@ public class BubblesTest extends SysuiTestCase { assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded); } + private Bubble eqBubbleWithKey(String key) { + return argThat(b -> b.getKey().equals(key)); + } + private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener { int mStateChangeCalls = 0; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS new file mode 100644 index 000000000000..eae8629231f6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/OWNERS @@ -0,0 +1,5 @@ +# Bubbles team +madym@google.com +atsjenk@google.com +liranb@google.com +mpodolian@google.com
\ No newline at end of file diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt index baaf60447cf9..703e2d1db200 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt @@ -49,8 +49,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos private val _isAlternateBouncerVisible = MutableStateFlow(false) override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow() override var lastAlternateBouncerVisibleTime: Long = 0L - private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false) - override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow() override val lastShownSecurityMode: MutableStateFlow<KeyguardSecurityModel.SecurityMode> = MutableStateFlow(KeyguardSecurityModel.SecurityMode.Invalid) override var bouncerDismissActionModel: BouncerDismissActionModel? = null @@ -63,10 +61,6 @@ class FakeKeyguardBouncerRepository @Inject constructor() : KeyguardBouncerRepos _isAlternateBouncerVisible.value = isVisible } - override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) { - _isAlternateBouncerUIAvailable.value = isAvailable - } - override fun setPrimaryShow(isShowing: Boolean) { _primaryBouncerShow.value = isShowing } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt index 63323b239a78..8bbb8a0d320e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorKosmos.kt @@ -16,33 +16,23 @@ package com.android.systemui.bouncer.domain.interactor -import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryBiometricsAllowedInteractor -import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor -import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope -import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.statusbar.policy.keyguardStateController -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.systemClock val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by Kosmos.Fixture { AlternateBouncerInteractor( - statusBarStateController = statusBarStateController, - keyguardStateController = keyguardStateController, bouncerRepository = keyguardBouncerRepository, fingerprintPropertyRepository = fingerprintPropertyRepository, - biometricSettingsRepository = biometricSettingsRepository, systemClock = systemClock, - keyguardUpdateMonitor = keyguardUpdateMonitor, deviceEntryBiometricsAllowedInteractor = { deviceEntryBiometricsAllowedInteractor }, keyguardInteractor = { keyguardInteractor }, keyguardTransitionInteractor = { keyguardTransitionInteractor }, @@ -54,21 +44,9 @@ val Kosmos.alternateBouncerInteractor: AlternateBouncerInteractor by fun Kosmos.givenCanShowAlternateBouncer() { this.givenAlternateBouncerSupported() this.keyguardBouncerRepository.setPrimaryShow(false) - this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true) - this.biometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true) this.deviceEntryFaceAuthRepository.setLockedOut(false) - whenever(this.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false) - whenever(this.keyguardStateController.isUnlocked).thenReturn(false) } fun Kosmos.givenAlternateBouncerSupported() { - if (DeviceEntryUdfpsRefactor.isEnabled) { - this.fingerprintPropertyRepository.supportsUdfps() - } else { - this.keyguardBouncerRepository.setAlternateBouncerUIAvailable(true) - } -} - -fun Kosmos.givenCannotShowAlternateBouncer() { - this.biometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(false) + this.fingerprintPropertyRepository.supportsUdfps() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt index d208465bd33d..6889b8ae41f9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt @@ -18,6 +18,7 @@ package com.android.systemui.brightness.ui.viewmodel import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforcementInteractor import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor +import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope @@ -27,5 +28,6 @@ val Kosmos.brightnessSliderViewModel: BrightnessSliderViewModel by screenBrightnessInteractor = screenBrightnessInteractor, brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor, applicationScope = applicationCoroutineScope, + hapticsViewModelFactory = sliderHapticsViewModelFactory, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt index fcc83b3e579f..78ea70086605 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt @@ -53,6 +53,10 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository { private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0) private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0) + suspend fun addDisplay(displayId: Int, type: Int = Display.TYPE_EXTERNAL) { + addDisplay(display(type, id = displayId)) + } + suspend fun addDisplay(display: Display) { flow.value += display displayAdditionEventFlow.emit(display) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.kt new file mode 100644 index 000000000000..257d758fe6e6 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/slider/SliderHapticsViewModelFactoryKosmos.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.haptics.slider + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.interaction.InteractionSource +import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel +import com.android.systemui.haptics.vibratorHelper +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.sliderHapticsViewModelFactory by + Kosmos.Fixture { + object : SliderHapticsViewModel.Factory { + override fun create( + interactionSource: InteractionSource, + sliderRange: ClosedFloatingPointRange<Float>, + orientation: Orientation, + sliderHapticFeedbackConfig: SliderHapticFeedbackConfig, + sliderTrackerConfig: SeekableSliderTrackerConfig, + ): SliderHapticsViewModel = + SliderHapticsViewModel( + interactionSource, + sliderRange, + orientation, + sliderHapticFeedbackConfig, + sliderTrackerConfig, + vibratorHelper, + fakeSystemClock, + ) + } + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt index 4ccee6f52fa2..2aa27444a058 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorKosmos.kt @@ -22,18 +22,16 @@ import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor val Kosmos.deviceEntrySideFpsOverlayInteractor: DeviceEntrySideFpsOverlayInteractor by Kosmos.Fixture { DeviceEntrySideFpsOverlayInteractor( - applicationScope = testScope.backgroundScope, context = applicationContext, deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository, sceneInteractor = sceneInteractor, primaryBouncerInteractor = primaryBouncerInteractor, alternateBouncerInteractor = alternateBouncerInteractor, - keyguardUpdateMonitor = keyguardUpdateMonitor + keyguardUpdateMonitor = keyguardUpdateMonitor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt index e4a2a874b848..b45120ee0aba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt @@ -37,11 +37,11 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum var hasUserActivity: Boolean = false private set - private var hasVibrator: Boolean = true - - private val state = VolumeDialogController.State() private val callbacks = CopyOnWriteArraySet<VolumeDialogController.Callbacks>() + private var hasVibrator: Boolean = true + private var state = VolumeDialogController.State() + override fun setActiveStream(stream: Int) { // ensure streamState existence for the active stream state.states.getOrElse(stream) { @@ -110,6 +110,11 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum hasUserActivity = false } + fun updateState(update: VolumeDialogController.State.() -> Unit) { + state = state.copy().apply(update) + getState() + } + override fun getState() { callbacks.sendEvent { it.onStateChanged(state) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt index ddcc6d60f993..b9f0c9a70d3d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeControllerKosmos.kt @@ -37,7 +37,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.deviceProvisionedController -import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.statusbar.window.StatusBarWindowControllerStore import com.android.systemui.util.mockito.mock import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -67,7 +67,7 @@ val Kosmos.shadeControllerImpl by mock<KeyguardStateController>(), statusBarStateController, statusBarKeyguardViewManager, - mock<StatusBarWindowController>(), + mock<StatusBarWindowControllerStore>(), deviceProvisionedController, mock<NotificationShadeWindowController>(), 0, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt index c0d65a076ca0..2316a2fdcd2b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ron/demo/ui/viewmodel/DemoRonChipViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel +package com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel import android.content.packageManager import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.util.time.fakeSystemClock -val Kosmos.demoRonChipViewModel: DemoRonChipViewModel by +val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by Kosmos.Fixture { - DemoRonChipViewModel( + DemoNotifChipViewModel( commandRegistry = commandRegistry, packageManager = packageManager, systemClock = fakeSystemClock, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt new file mode 100644 index 000000000000..af24c371d62b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.notification.ui.viewmodel + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor + +val Kosmos.notifChipsViewModel: NotifChipsViewModel by + Kosmos.Fixture { NotifChipsViewModel(activeNotificationsInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 5382c1c4b8d0..0300bf4636ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -20,7 +20,8 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.chips.call.ui.viewmodel.callChipViewModel import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.castToOtherDeviceChipViewModel -import com.android.systemui.statusbar.chips.ron.demo.ui.viewmodel.demoRonChipViewModel +import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel +import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel import com.android.systemui.statusbar.chips.statusBarChipsLogger @@ -33,7 +34,8 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by shareToAppChipViewModel = shareToAppChipViewModel, castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel, callChipViewModel = callChipViewModel, - demoRonChipViewModel = demoRonChipViewModel, + notifChipsViewModel = notifChipsViewModel, + demoNotifChipViewModel = demoNotifChipViewModel, logger = statusBarChipsLogger, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt new file mode 100644 index 000000000000..73ed228f5aaa --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.core + +import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions +import com.android.systemui.statusbar.phone.PhoneStatusBarViewController + +class FakeStatusBarInitializerFactory( + private val statusBarViewController: PhoneStatusBarViewController, + private val statusBarTransitions: PhoneStatusBarTransitions, +) : StatusBarInitializer.Factory { + + override fun create(displayId: Int): StatusBarInitializer = + FakeStatusBarInitializer(statusBarViewController, statusBarTransitions) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt index d10320004454..7ad715ba5a89 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt @@ -16,7 +16,9 @@ package com.android.systemui.statusbar.core +import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.statusbar.phone.phoneStatusBarTransitions import com.android.systemui.statusbar.phone.phoneStatusBarViewController @@ -26,3 +28,20 @@ val Kosmos.fakeStatusBarInitializer by } var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer } + +val Kosmos.fakeStatusBarInitializerFactory by + Kosmos.Fixture { + FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions) + } + +var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by + Kosmos.Fixture { fakeStatusBarInitializerFactory } + +val Kosmos.multiDisplayStatusBarInitializerStore by + Kosmos.Fixture { + MultiDisplayStatusBarInitializerStore( + applicationCoroutineScope, + fakeStatusBarInitializerFactory, + displayRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt index c53e44d514f7..54de293b8911 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt @@ -28,7 +28,7 @@ import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepositor import com.android.systemui.statusbar.mockNotificationRemoteInputManager import com.android.systemui.statusbar.phone.mockAutoHideController import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore -import com.android.systemui.statusbar.window.fakeStatusBarWindowController +import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore import com.android.wm.shell.bubbles.bubblesOptional val Kosmos.statusBarOrchestrator by @@ -36,8 +36,8 @@ val Kosmos.statusBarOrchestrator by StatusBarOrchestrator( applicationCoroutineScope, fakeStatusBarInitializer, - fakeStatusBarWindowController, fakeStatusBarModeRepository, + fakeStatusBarWindowControllerStore, mockDemoModeController, mockPluginDependencyProvider, mockAutoHideController, 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 fc4f05df26ed..a7a61957d104 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 @@ -69,6 +69,8 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag +import com.android.systemui.statusbar.notification.row.icon.AppIconProviderImpl +import com.android.systemui.statusbar.notification.row.icon.NotificationRowIconViewInflaterFactory import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger import com.android.systemui.statusbar.phone.KeyguardBypassController @@ -99,7 +101,7 @@ import org.mockito.Mockito class ExpandableNotificationRowBuilder( private val context: Context, dependency: TestableDependency, - private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic() + private val featureFlags: FakeFeatureFlagsClassic = FakeFeatureFlagsClassic(), ) { private val mMockLogger: ExpandableNotificationRowLogger @@ -161,21 +163,21 @@ class ExpandableNotificationRowBuilder( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "true", - true + true, ) setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_ENABLED, "true", - true + true, ) setProperty( DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "false", - true + true, ) - } + }, ) val remoteViewsFactories = getNotifRemoteViewsFactoryContainer(featureFlags) val remoteInputManager = Mockito.mock(NotificationRemoteInputManager::class.java, STUB_ONLY) @@ -192,21 +194,21 @@ class ExpandableNotificationRowBuilder( Mockito.mock(KeyguardDismissUtil::class.java, STUB_ONLY), remoteInputManager = remoteInputManager, smartReplyController = mSmartReplyController, - context = context + context = context, ), smartActionsInflater = SmartActionInflaterImpl( constants = mSmartReplyConstants, activityStarter = Mockito.mock(ActivityStarter::class.java, STUB_ONLY), smartReplyController = mSmartReplyController, - headsUpManager = mHeadsUpManager - ) + headsUpManager = mHeadsUpManager, + ), ) val notifLayoutInflaterFactoryProvider = object : NotifLayoutInflaterFactory.Provider { override fun provide( row: ExpandableNotificationRow, - layoutType: Int + layoutType: Int, ): NotifLayoutInflaterFactory = NotifLayoutInflaterFactory(row, layoutType, remoteViewsFactories) } @@ -270,14 +272,14 @@ class ExpandableNotificationRowBuilder( whenever( mOnUserInteractionCallback.registerFutureDismissal( ArgumentMatchers.any(), - ArgumentMatchers.anyInt() + ArgumentMatchers.anyInt(), ) ) .thenReturn(mFutureDismissalRunnable) } private fun getNotifRemoteViewsFactoryContainer( - featureFlags: FeatureFlags, + featureFlags: FeatureFlags ): NotifRemoteViewsFactoryContainer { return NotifRemoteViewsFactoryContainerImpl( featureFlags, @@ -285,6 +287,7 @@ class ExpandableNotificationRowBuilder( BigPictureLayoutInflaterFactory(), NotificationOptimizedLinearLayoutFactory(), { Mockito.mock(NotificationViewFlipperFactory::class.java) }, + NotificationRowIconViewInflaterFactory(AppIconProviderImpl(context)), ) } @@ -293,7 +296,7 @@ class ExpandableNotificationRowBuilder( NotificationChannel( notification.channelId, notification.channelId, - NotificationManager.IMPORTANCE_DEFAULT + NotificationManager.IMPORTANCE_DEFAULT, ) channel.isBlockable = true val entry = @@ -321,7 +324,7 @@ class ExpandableNotificationRowBuilder( private fun generateRow( entry: NotificationEntry, - @InflationFlag extraInflationFlags: Int + @InflationFlag extraInflationFlags: Int, ): ExpandableNotificationRow { // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be // set, but we do not want to override an existing value that is needed by a specific test. @@ -329,7 +332,7 @@ class ExpandableNotificationRowBuilder( val rowInflaterTask = RowInflaterTask( mFakeSystemClock, - Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY) + Mockito.mock(RowInflaterTaskLogger::class.java, STUB_ONLY), ) val row = rowInflaterTask.inflateSynchronously(context, null, entry) @@ -364,7 +367,7 @@ class ExpandableNotificationRowBuilder( mSmartReplyController, featureFlags, Mockito.mock(IStatusBarService::class.java, STUB_ONLY), - Mockito.mock(UiEventLogger::class.java, STUB_ONLY) + Mockito.mock(UiEventLogger::class.java, STUB_ONLY), ) row.setAboveShelfChangedListener { aboveShelf: Boolean -> } mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt new file mode 100644 index 000000000000..08c6bbab6dd6 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row.icon + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt index d19e3227027c..35f95b6fab8f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerStore.kt @@ -22,10 +22,10 @@ class FakeStatusBarWindowControllerStore : StatusBarWindowControllerStore { private val perDisplayControllers = mutableMapOf<Int, FakeStatusBarWindowController>() - override val defaultDisplay + override val defaultDisplay: FakeStatusBarWindowController get() = forDisplay(Display.DEFAULT_DISPLAY) - override fun forDisplay(displayId: Int): StatusBarWindowController { + override fun forDisplay(displayId: Int): FakeStatusBarWindowController { return perDisplayControllers.computeIfAbsent(displayId) { FakeStatusBarWindowController() } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt index 6c6f243f3953..78caf93d4618 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt @@ -20,7 +20,8 @@ import com.android.systemui.kosmos.Kosmos val Kosmos.fakeStatusBarWindowController by Kosmos.Fixture { FakeStatusBarWindowController() } -var Kosmos.statusBarWindowController by Kosmos.Fixture { fakeStatusBarWindowController } +var Kosmos.statusBarWindowController: StatusBarWindowController by + Kosmos.Fixture { fakeStatusBarWindowController } val Kosmos.fakeStatusBarWindowControllerStore by Kosmos.Fixture { FakeStatusBarWindowControllerStore() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepositoryKosmos.kt new file mode 100644 index 000000000000..768111170f10 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/data/repository/VolumeDialogStateRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.volumeDialogStateRepository: VolumeDialogStateRepository by + Kosmos.Fixture { VolumeDialogStateRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractorKosmos.kt new file mode 100644 index 000000000000..8944861bc5de --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractorKosmos.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.volume.dialog.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.plugins.volumeDialogController +import com.android.systemui.volume.dialog.data.repository.volumeDialogStateRepository + +val Kosmos.volumeDialogStateInteractor: VolumeDialogStateInteractor by + Kosmos.Fixture { + VolumeDialogStateInteractor( + volumeDialogCallbacksInteractor, + volumeDialogController, + volumeDialogStateRepository, + applicationCoroutineScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorKosmos.kt new file mode 100644 index 000000000000..0f573d9160b9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.domain.interactor + +import android.content.packageManager +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor + +val Kosmos.volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor by + Kosmos.Fixture { + VolumeDialogSlidersInteractor( + volumeDialogStateInteractor, + packageManager, + applicationCoroutineScope, + ) + } diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 8896d772ea4d..bfa801f30955 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -8,7 +8,7 @@ package { // OWNER: g/ravenwood // Bug component: 25698 - default_team: "trendy_team_framework_backstage_power", + default_team: "trendy_team_ravenwood", } filegroup { 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 908e5903122e..5894476b9201 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -335,7 +335,11 @@ public class RavenwoodRuntimeEnvironmentController { } android.os.Process.reset$ravenwood(); - ResourcesManager.setInstance(null); // Better structure needed. + try { + ResourcesManager.setInstance(null); // Better structure needed. + } catch (Exception e) { + // AOSP-CHANGE: AOSP doesn't support resources yet. + } if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { maybeThrowPendingUncaughtException(true); diff --git a/ravenwood/minimum-test/Android.bp b/ravenwood/tests/minimum-test/Android.bp index e4ed3d5b0b73..e4ed3d5b0b73 100644 --- a/ravenwood/minimum-test/Android.bp +++ b/ravenwood/tests/minimum-test/Android.bp diff --git a/ravenwood/minimum-test/README.md b/ravenwood/tests/minimum-test/README.md index 6b0abe968053..6b0abe968053 100644 --- a/ravenwood/minimum-test/README.md +++ b/ravenwood/tests/minimum-test/README.md diff --git a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java index 30abaa2e7d38..30abaa2e7d38 100644 --- a/ravenwood/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java +++ b/ravenwood/tests/minimum-test/test/com/android/ravenwoodtest/RavenwoodMinimumTest.java diff --git a/ravenwood/mockito/Android.bp b/ravenwood/tests/mockito/Android.bp index d91537bbc334..d91537bbc334 100644 --- a/ravenwood/mockito/Android.bp +++ b/ravenwood/tests/mockito/Android.bp diff --git a/ravenwood/mockito/AndroidManifest.xml b/ravenwood/tests/mockito/AndroidManifest.xml index 15f0a2934b5f..15f0a2934b5f 100644 --- a/ravenwood/mockito/AndroidManifest.xml +++ b/ravenwood/tests/mockito/AndroidManifest.xml diff --git a/ravenwood/mockito/AndroidTest.xml b/ravenwood/tests/mockito/AndroidTest.xml index 5ba9b1ff2cd8..5ba9b1ff2cd8 100644 --- a/ravenwood/mockito/AndroidTest.xml +++ b/ravenwood/tests/mockito/AndroidTest.xml diff --git a/ravenwood/mockito/README.md b/ravenwood/tests/mockito/README.md index 4ceb795fe14a..4ceb795fe14a 100644 --- a/ravenwood/mockito/README.md +++ b/ravenwood/tests/mockito/README.md diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java index d566977bd15c..d566977bd15c 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoDeviceOnlyTest.java diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java index aa2b7611da37..aa2b7611da37 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoRavenwoodOnlyTest.java diff --git a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java index fcc6c9cc447d..fcc6c9cc447d 100644 --- a/ravenwood/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java +++ b/ravenwood/tests/mockito/test/com/android/ravenwoodtest/mockito/RavenwoodMockitoTest.java diff --git a/ravenwood/resapk_test/Android.bp b/ravenwood/tests/resapk_test/Android.bp index c14576550f78..c14576550f78 100644 --- a/ravenwood/resapk_test/Android.bp +++ b/ravenwood/tests/resapk_test/Android.bp diff --git a/ravenwood/resapk_test/apk/Android.bp b/ravenwood/tests/resapk_test/apk/Android.bp index 10ed5e2f8410..10ed5e2f8410 100644 --- a/ravenwood/resapk_test/apk/Android.bp +++ b/ravenwood/tests/resapk_test/apk/Android.bp diff --git a/ravenwood/resapk_test/apk/AndroidManifest.xml b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml index f34d8b2f4e81..f34d8b2f4e81 100644 --- a/ravenwood/resapk_test/apk/AndroidManifest.xml +++ b/ravenwood/tests/resapk_test/apk/AndroidManifest.xml diff --git a/ravenwood/resapk_test/apk/res/values/strings.xml b/ravenwood/tests/resapk_test/apk/res/values/strings.xml index 23d4c0f21007..23d4c0f21007 100644 --- a/ravenwood/resapk_test/apk/res/values/strings.xml +++ b/ravenwood/tests/resapk_test/apk/res/values/strings.xml diff --git a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java index e547114bbe40..e547114bbe40 100644 --- a/ravenwood/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java +++ b/ravenwood/tests/resapk_test/test/com/android/ravenwoodtest/resapk_test/RavenwoodResApkTest.java diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp index 410292001670..410292001670 100644 --- a/ravenwood/runtime-test/Android.bp +++ b/ravenwood/tests/runtime-test/Android.bp diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java index 633ed4e9d10a..633ed4e9d10a 100644 --- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsConstantsTest.java diff --git a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java index c2230c739ccf..c2230c739ccf 100644 --- a/ravenwood/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java diff --git a/ravenwood/services-test/Android.bp b/ravenwood/tests/services-test/Android.bp index 39858f05e80d..39858f05e80d 100644 --- a/ravenwood/services-test/Android.bp +++ b/ravenwood/tests/services-test/Android.bp diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java index f833782bc8bb..f833782bc8bb 100644 --- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesDependenciesTest.java diff --git a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java index b3d3963270ee..b3d3963270ee 100644 --- a/ravenwood/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java +++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ba8448548365..1803424ae7d7 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -49,3 +49,10 @@ flag { description: "Include the session id into the FillEventHistory events as part of ClientState" bug: "333927465" } + +flag { + name: "highlight_autofill_single_field" + namespace: "autofill" + description: "Highlight single field after autofill selection" + bug: "41496744" +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 2fa0e0d0d946..8f12b1db8f29 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -39,6 +39,7 @@ import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; 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.FillRequest.INVALID_REQUEST_ID; +import static android.service.autofill.Flags.highlightAutofillSingleField; 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; @@ -49,7 +50,6 @@ import static android.view.autofill.AutofillManager.COMMIT_REASON_UNKNOWN; 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; @@ -104,7 +104,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Activity; import android.app.ActivityTaskManager; import android.app.IAssistDataReceiver; import android.app.PendingIntent; @@ -143,7 +142,6 @@ import android.os.TransactionTooLargeException; import android.service.assist.classification.FieldClassificationRequest; import android.service.assist.classification.FieldClassificationResponse; import android.service.autofill.AutofillFieldClassificationService.Scores; -import android.service.autofill.AutofillService; import android.service.autofill.CompositeUserData; import android.service.autofill.ConvertCredentialResponse; import android.service.autofill.Dataset; @@ -186,7 +184,6 @@ import android.view.autofill.IAutoFillManagerClient; 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; @@ -198,7 +195,6 @@ import com.android.server.autofill.ui.InlineFillUi; 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; @@ -222,18 +218,21 @@ import java.util.function.Function; /** * A session for a given activity. * - * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track - * of the current {@link ViewState} to display the appropriate UI. + * <p>This class manages the multiple {@link ViewState}s for each view it has, and keeps track of + * the current {@link ViewState} to display the appropriate UI. * - * <p>Although the autofill requests and callbacks are stateless from the service's point of - * view, we need to keep state in the framework side for cases such as authentication. For - * example, when service return a {@link FillResponse} that contains all the fields needed - * to fill the activity but it requires authentication first, that response need to be held - * until the user authenticates or it times out. + * <p>Although the autofill requests and callbacks are stateless from the service's point of view, + * we need to keep state in the framework side for cases such as authentication. For example, when + * service return a {@link FillResponse} that contains all the fields needed to fill the activity + * but it requires authentication first, that response need to be held until the user authenticates + * or it times out. */ -final class Session implements RemoteFillService.FillServiceCallbacks, ViewState.Listener, - AutoFillUI.AutoFillUiCallback, ValueFinder, - RemoteFieldClassificationService.FieldClassificationServiceCallbacks { +final class Session + implements RemoteFillService.FillServiceCallbacks, + ViewState.Listener, + AutoFillUI.AutoFillUiCallback, + ValueFinder, + RemoteFieldClassificationService.FieldClassificationServiceCallbacks { private static final String TAG = "AutofillSession"; // This should never be true in production. This is only for local debugging. @@ -257,6 +256,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final AutofillManagerServiceImpl mService; private final Handler mHandler; private final AutoFillUI mUi; + /** * Context associated with the session, it has the same {@link Context#getDisplayId() displayId} * of the activity being autofilled. @@ -286,14 +286,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** Session is destroyed and removed from the manager service. */ public static final int STATE_REMOVED = 3; - @IntDef(prefix = { "STATE_" }, value = { - STATE_UNKNOWN, - STATE_ACTIVE, - STATE_FINISHED, - STATE_REMOVED - }) + @IntDef( + prefix = {"STATE_"}, + value = {STATE_UNKNOWN, STATE_ACTIVE, STATE_FINISHED, STATE_REMOVED}) @Retention(RetentionPolicy.SOURCE) - @interface SessionState{} + @interface SessionState {} @GuardedBy("mLock") private final SessionFlags mSessionFlags; @@ -318,7 +315,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public final int mFlags; @GuardedBy("mLock") - @NonNull private IBinder mActivityToken; + @NonNull + private IBinder mActivityToken; /** The app activity that's being autofilled */ @NonNull private final ComponentName mComponentName; @@ -341,11 +339,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * autofill. */ @GuardedBy("mLock") - @Nullable private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest; + @Nullable + private Pair<Integer, InlineSuggestionsRequest> mLastInlineSuggestionsRequest; - /** - * Id of the View currently being displayed. - */ + /** Id of the View currently being displayed. */ @GuardedBy("mLock") private @Nullable AutofillId mCurrentViewId; @@ -363,8 +360,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * * <p>Only {@code null} when the session is for augmented autofill only. */ - @Nullable - private final RemoteFillService mRemoteFillService; + @Nullable private final RemoteFillService mRemoteFillService; /** * With the credman integration, Autofill Framework handles two types of autofill flows - @@ -379,8 +375,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * service the session was initially created with, the secondary provider handler will contain * the remaining autofill service. */ - @Nullable - private final SecondaryProviderHandler mSecondaryProviderHandler; + @Nullable private final SecondaryProviderHandler mSecondaryProviderHandler; @GuardedBy("mLock") private SparseArray<FillResponse> mResponses; @@ -395,9 +390,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<FillContext> mContexts; - /** - * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. - */ + /** Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}. */ private boolean mHasCallback; /** Whether the session has credential manager provider as the primary provider. */ @@ -426,32 +419,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private PendingUi mPendingSaveUi; - /** - * List of dataset ids selected by the user. - */ + /** List of dataset ids selected by the user. */ @GuardedBy("mLock") private ArrayList<String> mSelectedDatasetIds; - /** - * When the session started (using elapsed time since boot). - */ + /** When the session started (using elapsed time since boot). */ private final long mStartTime; - /** - * Count of FillRequests in the session. - */ + /** Count of FillRequests in the session. */ private int mRequestCount; /** - * Starting timestamp of latency logger. - * This is set when Session created or when the view is reset. + * Starting timestamp of latency logger. This is set when Session created or when the view is + * reset. */ @GuardedBy("mLock") private long mLatencyBaseTime; - /** - * When the UI was shown for the first time (using elapsed time since boot). - */ + /** When the UI was shown for the first time (using elapsed time since boot). */ @GuardedBy("mLock") private long mUiShownTime; @@ -475,15 +460,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private final LocalLog mWtfHistory; - /** - * Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. - */ + /** Map of {@link MetricsEvent#AUTOFILL_REQUEST} metrics, keyed by fill request id. */ @GuardedBy("mLock") private final SparseArray<LogMaker> mRequestLogs = new SparseArray<>(1); - /** - * Destroys the augmented Autofill UI. - */ + /** Destroys the augmented Autofill UI. */ // TODO(b/123099468): this runnable is called when the Autofill session is destroyed, the // main reason being the cases where user tap HOME. // Right now it's completely destroying the UI, but we need to decide whether / how to @@ -494,13 +475,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable private Runnable mAugmentedAutofillDestroyer; - /** - * List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. - */ + /** List of {@link MetricsEvent#AUTOFILL_AUGMENTED_REQUEST} metrics. */ @GuardedBy("mLock") private ArrayList<LogMaker> mAugmentedRequestsLogs; - /** * List of autofill ids of autofillable fields present in the AssistStructure that can be used * to trigger new augmented autofill requests (because the "standard" service was not interested @@ -509,23 +487,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private ArrayList<AutofillId> mAugmentedAutofillableIds; - @NonNull - final AutofillInlineSessionController mInlineSessionController; + @NonNull final AutofillInlineSessionController mInlineSessionController; - /** - * Receiver of assist data from the app's {@link Activity}. - */ + /** Receiver of assist data from the app's {@link Activity}. */ private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl(); - /** - * Receiver of assist data for pcc purpose - */ + /** Receiver of assist data for pcc purpose */ private final PccAssistDataReceiverImpl mPccAssistReceiver = new PccAssistDataReceiverImpl(); private final ClassificationState mClassificationState = new ClassificationState(); - @Nullable - private final ComponentName mCredentialAutofillService; + @Nullable private final ComponentName mCredentialAutofillService; // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a // new one per Session. @@ -547,7 +519,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received"); synchronized (mLock) { int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0); - FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE, android.service.autofill.FillResponse.class); + FillResponse response = + intent.getParcelableExtra( + EXTRA_FILL_RESPONSE, + android.service.autofill.FillResponse.class); mFillRequestEventLogger.maybeSetRequestTriggerReason( TRIGGER_REASON_RETRIGGER); mAssistReceiver.processDelayedFillLocked(requestId, response); @@ -575,28 +550,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private SessionCommittedEventLogger mSessionCommittedEventLogger; - /** - * Fill dialog request would likely be sent slightly later. - */ + /** Fill dialog request would likely be sent slightly later. */ @NonNull @GuardedBy("mLock") private boolean mPreviouslyFillDialogPotentiallyStarted; /** - * Keeps track of if the user entered view, this is used to - * distinguish Fill Request that did not have user interaction - * with ones that did. + * Keeps track of if the user entered view, this is used to distinguish Fill Request that did + * not have user interaction with ones that did. * - * This is set to true when entering view - after FillDialog FillRequest - * or on plain user tap. + * <p>This is set to true when entering view - after FillDialog FillRequest or on plain user + * tap. */ @NonNull @GuardedBy("mLock") private boolean mLogViewEntered; /** - * Keeps the fill dialog trigger ids of the last response. This invalidates - * the trigger ids of the previous response. + * Keeps the fill dialog trigger ids of the last response. This invalidates the trigger ids of + * the previous response. */ @Nullable @GuardedBy("mLock") @@ -662,9 +634,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return false; } - /** - * Collection of flags/booleans that helps determine Session behaviors. - */ + /** Collection of flags/booleans that helps determine Session behaviors. */ private final class SessionFlags { /** Whether autofill is disabled by the service */ private boolean mAutofillDisabled; @@ -695,31 +665,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") private boolean mWaitForInlineRequest; + @GuardedBy("mLock") private InlineSuggestionsRequest mPendingInlineSuggestionsRequest; + @GuardedBy("mLock") private FillRequest mPendingFillRequest; + @GuardedBy("mLock") private FillRequest mLastFillRequest; - @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState, - boolean isInlineRequest) { + @Nullable + Consumer<InlineSuggestionsRequest> newAutofillRequestLocked( + ViewState viewState, boolean isInlineRequest) { mPendingFillRequest = null; mWaitForInlineRequest = isInlineRequest; mPendingInlineSuggestionsRequest = null; if (isInlineRequest) { WeakReference<AssistDataReceiverImpl> assistDataReceiverWeakReference = - new WeakReference<AssistDataReceiverImpl>(this); + new WeakReference<AssistDataReceiverImpl>(this); WeakReference<ViewState> viewStateWeakReference = - new WeakReference<ViewState>(viewState); - return new InlineSuggestionRequestConsumer(assistDataReceiverWeakReference, - viewStateWeakReference); + new WeakReference<ViewState>(viewState); + return new InlineSuggestionRequestConsumer( + assistDataReceiverWeakReference, viewStateWeakReference); } return null; } - void handleInlineSuggestionRequest(InlineSuggestionsRequest inlineSuggestionsRequest, - ViewState viewState) { + void handleInlineSuggestionRequest( + InlineSuggestionsRequest inlineSuggestionsRequest, ViewState viewState) { if (sVerbose) { Slog.v(TAG, "handleInlineSuggestionRequest(): inline suggestion request received"); } @@ -738,8 +712,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void maybeRequestFillLocked() { if (mPendingFillRequest == null) { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request " - + "due to empty pending fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): cancelling calling fill request " + + "due to empty pending fill request"); } return; } @@ -748,27 +724,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mWaitForInlineRequest) { if (mPendingInlineSuggestionsRequest == null) { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): cancelling calling fill request " - + "due to waiting for inline request and pending inline request is " - + "currently empty"); + Slog.v( + TAG, + "maybeRequestFillLocked(): cancelling calling fill request due to" + + " waiting for inline request and pending inline request is" + + " currently empty"); } return; } if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): adding inline request to pending " - + "fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): adding inline request to pending " + + "fill request"); } - mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), - mPendingFillRequest.getFillContexts(), - mPendingFillRequest.getHints(), - mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), - mPendingInlineSuggestionsRequest, - mPendingFillRequest.getDelayedFillIntentSender()); + mPendingFillRequest = + new FillRequest( + mPendingFillRequest.getId(), + mPendingFillRequest.getFillContexts(), + mPendingFillRequest.getHints(), + mPendingFillRequest.getClientState(), + mPendingFillRequest.getFlags(), + mPendingInlineSuggestionsRequest, + mPendingFillRequest.getDelayedFillIntentSender()); } else { if (sVerbose) { - Slog.v(TAG, "maybeRequestFillLocked(): not adding inline request to pending " - + "fill request"); + Slog.v( + TAG, + "maybeRequestFillLocked(): not adding inline request to pending " + + "fill request"); } } @@ -780,19 +764,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && mSecondaryProviderHandler != null) { Slog.v(TAG, "Requesting fill response to secondary provider."); if (!mIsPrimaryCredential) { - mPendingFillRequest = addCredentialManagerDataToClientState( - mPendingFillRequest, - mPendingInlineSuggestionsRequest, id); + mPendingFillRequest = + addCredentialManagerDataToClientState( + mPendingFillRequest, mPendingInlineSuggestionsRequest, id); } - mSecondaryProviderHandler.onFillRequest(mPendingFillRequest, - mPendingFillRequest.getFlags(), mClient.asBinder()); + mSecondaryProviderHandler.onFillRequest( + mPendingFillRequest, mPendingFillRequest.getFlags(), mClient.asBinder()); } else if (mRemoteFillService != null) { if (mIsPrimaryCredential) { - mPendingFillRequest = addCredentialManagerDataToClientState( - mPendingFillRequest, - mPendingInlineSuggestionsRequest, id); - mRemoteFillService.onFillCredentialRequest(mPendingFillRequest, - mClient.asBinder()); + mPendingFillRequest = + addCredentialManagerDataToClientState( + mPendingFillRequest, mPendingInlineSuggestionsRequest, id); + mRemoteFillService.onFillCredentialRequest( + mPendingFillRequest, mClient.asBinder()); } else { mRemoteFillService.onFillRequest(mPendingFillRequest); } @@ -813,8 +797,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { if (mRemoteFillService == null) { - wtf(null, "onHandleAssistData() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "onHandleAssistData() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } // Keeps to prevent it is cleared on multiple threads. @@ -824,7 +811,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); + final AssistStructure structure = + resultData.getParcelable( + ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); if (structure == null) { Slog.e(TAG, "No assist structure - app might have crashed providing it"); return; @@ -852,13 +841,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { structure.ensureDataForAutofill(); } catch (RuntimeException e) { - wtf(e, "Exception lazy loading assist structure for %s: %s", - structure.getActivityComponent(), e); + wtf( + e, + "Exception lazy loading assist structure for %s: %s", + structure.getActivityComponent(), + e); return; } - final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, - /* autofillableOnly= */false); + final ArrayList<AutofillId> ids = + Helper.getAutofillIds(structure, /* autofillableOnly= */ false); for (int i = 0; i < ids.size(); i++) { ids.get(i).setSessionId(Session.this.id); } @@ -868,8 +860,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mCompatMode) { // Sanitize URL bar, if needed - final String[] urlBarIds = mService.getUrlBarResourceIdsForCompatMode( - mComponentName.getPackageName()); + final String[] urlBarIds = + mService.getUrlBarResourceIdsForCompatMode( + mComponentName.getPackageName()); if (sDebug) { Slog.d(TAG, "url_bars in compat mode: " + Arrays.toString(urlBarIds)); } @@ -878,11 +871,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mUrlBar != null) { final AutofillId urlBarId = mUrlBar.getAutofillId(); if (sDebug) { - Slog.d(TAG, "Setting urlBar as id=" + urlBarId + " and domain " - + mUrlBar.getWebDomain()); + Slog.d( + TAG, + "Setting urlBar as id=" + + urlBarId + + " and domain " + + mUrlBar.getWebDomain()); } - final ViewState viewState = new ViewState(urlBarId, Session.this, - ViewState.STATE_URL_BAR, mIsPrimaryCredential); + final ViewState viewState = + new ViewState( + urlBarId, + Session.this, + ViewState.STATE_URL_BAR, + mIsPrimaryCredential); mViewStates.put(urlBarId, viewState); } } @@ -907,11 +908,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<String> hints = getTypeHintsForProvider(); mDelayedFillPendingIntent = createPendingIntent(requestId); - request = new FillRequest(requestId, contexts, hints, mClientState, flags, - /*inlineSuggestionsRequest=*/ null, - /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null - ? null - : mDelayedFillPendingIntent.getIntentSender()); + request = + new FillRequest( + requestId, + contexts, + hints, + mClientState, + flags, + /* inlineSuggestionsRequest= */ null, + /* delayedFillIntentSender= */ mDelayedFillPendingIntent == null + ? null + : mDelayedFillPendingIntent.getIntentSender()); mPendingFillRequest = request; maybeRequestFillLocked(); @@ -930,39 +937,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void processDelayedFillLocked(int requestId, FillResponse response) { if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) { - Slog.v(TAG, "processDelayedFillLocked: " - + "calling onFillRequestSuccess with new response"); - onFillRequestSuccess(requestId, response, - mService.getServicePackageName(), mLastFillRequest.getFlags()); + Slog.v( + TAG, + "processDelayedFillLocked: " + + "calling onFillRequestSuccess with new response"); + onFillRequestSuccess( + requestId, + response, + mService.getServicePackageName(), + mLastFillRequest.getFlags()); } } } - private FillRequest addCredentialManagerDataToClientState(FillRequest pendingFillRequest, - InlineSuggestionsRequest pendingInlineSuggestionsRequest, int sessionId) { + private FillRequest addCredentialManagerDataToClientState( + FillRequest pendingFillRequest, + InlineSuggestionsRequest pendingInlineSuggestionsRequest, + int sessionId) { if (pendingFillRequest.getClientState() == null) { - pendingFillRequest = new FillRequest(pendingFillRequest.getId(), - pendingFillRequest.getFillContexts(), - pendingFillRequest.getHints(), - new Bundle(), - pendingFillRequest.getFlags(), - pendingInlineSuggestionsRequest, - pendingFillRequest.getDelayedFillIntentSender()); + pendingFillRequest = + new FillRequest( + pendingFillRequest.getId(), + pendingFillRequest.getFillContexts(), + pendingFillRequest.getHints(), + new Bundle(), + pendingFillRequest.getFlags(), + pendingInlineSuggestionsRequest, + pendingFillRequest.getDelayedFillIntentSender()); } pendingFillRequest.getClientState().putInt(SESSION_ID_KEY, sessionId); pendingFillRequest.getClientState().putInt(REQUEST_ID_KEY, pendingFillRequest.getId()); - ResultReceiver resultReceiver = constructCredentialManagerCallback( - pendingFillRequest.getId()); - pendingFillRequest.getClientState().putParcelable( - CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver); + ResultReceiver resultReceiver = + constructCredentialManagerCallback(pendingFillRequest.getId()); + pendingFillRequest + .getClientState() + .putParcelable(CredentialManager.EXTRA_AUTOFILL_RESULT_RECEIVER, resultReceiver); return pendingFillRequest; } /** - * Get the list of valid autofill hint types from Device flags - * Returns empty list if PCC is off or no types available - */ + * Get the list of valid autofill hint types from Device flags Returns empty list if PCC is off + * or no types available + */ private List<String> getTypeHintsForProvider() { if (!mService.isPccClassificationEnabled()) { return Collections.EMPTY_LIST; @@ -978,9 +995,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return List.of(typeHints.split(PCC_HINTS_DELIMITER)); } - /** - * Assist Data Receiver for PCC - */ + /** Assist Data Receiver for PCC */ private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub { @GuardedBy("mLock") @@ -998,7 +1013,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState new WeakReference<>(Session.this); remoteFieldClassificationService.onFieldClassificationRequest( mClassificationState.mPendingFieldClassificationRequest, - fieldClassificationServiceCallbacksWeakRef); + fieldClassificationServiceCallbacksWeakRef); } mClassificationState.onFieldClassificationRequestSent(); } @@ -1006,26 +1021,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void onHandleAssistData(Bundle resultData) throws RemoteException { // TODO: add a check if pcc field classification service is present - final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE, - android.app.assist.AssistStructure.class); + final AssistStructure structure = + resultData.getParcelable( + ASSIST_KEY_STRUCTURE, android.app.assist.AssistStructure.class); if (structure == null) { - Slog.e(TAG, "No assist structure for pcc detection - " - + "app might have crashed providing it"); + Slog.e( + TAG, + "No assist structure for pcc detection - " + + "app might have crashed providing it"); return; } final Bundle receiverExtras = resultData.getBundle(ASSIST_KEY_RECEIVER_EXTRAS); if (receiverExtras == null) { - Slog.e(TAG, "No receiver extras for pcc detection - " - + "app might have crashed providing it"); + Slog.e( + TAG, + "No receiver extras for pcc detection - " + + "app might have crashed providing it"); return; } final int requestId = receiverExtras.getInt(EXTRA_REQUEST_ID); if (sVerbose) { - Slog.v(TAG, "New structure for PCC Detection: requestId " + requestId + ": " - + structure); + Slog.v( + TAG, + "New structure for PCC Detection: requestId " + + requestId + + ": " + + structure); } synchronized (mLock) { @@ -1037,13 +1061,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { structure.ensureDataForAutofill(); } catch (RuntimeException e) { - wtf(e, "Exception lazy loading assist structure for %s: %s", - structure.getActivityComponent(), e); + wtf( + e, + "Exception lazy loading assist structure for %s: %s", + structure.getActivityComponent(), + e); return; } - final ArrayList<AutofillId> ids = Helper.getAutofillIds(structure, - /* autofillableOnly= */false); + final ArrayList<AutofillId> ids = + Helper.getAutofillIds(structure, /* autofillableOnly= */ false); for (int i = 0; i < ids.size(); i++) { ids.get(i).setSessionId(Session.this.id); } @@ -1066,13 +1093,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState PendingIntent pendingIntent; final long identity = Binder.clearCallingIdentity(); try { - Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android") - .putExtra(EXTRA_REQUEST_ID, requestId); - pendingIntent = PendingIntent.getBroadcast( - mContext, this.id, intent, - PendingIntent.FLAG_MUTABLE - | PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT); + Intent intent = + new Intent(ACTION_DELAYED_FILL) + .setPackage("android") + .putExtra(EXTRA_REQUEST_ID, requestId); + pendingIntent = + PendingIntent.getBroadcast( + mContext, + this.id, + intent, + PendingIntent.FLAG_MUTABLE + | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT); } finally { Binder.restoreCallingIdentity(identity); } @@ -1113,9 +1145,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Returns the ids of all entries in {@link #mViewStates} in the same order. - */ + /** Returns the ids of all entries in {@link #mViewStates} in the same order. */ @GuardedBy("mLock") private AutofillId[] getIdsOfAllViewStatesLocked() { final int numViewState = mViewStates.size(); @@ -1164,8 +1194,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, - * or {@code null} when not found on either of them. + * Gets the value of a field, using either the {@code viewStates} or the {@code mContexts}, or + * {@code null} when not found on either of them. */ @GuardedBy("mLock") @Nullable @@ -1180,16 +1210,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this); if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size() - + " previous sessions for autofillId " + autofillId); + Slog.d( + TAG, + "findValueLocked(): looking on " + + previousSessions.size() + + " previous sessions for autofillId " + + autofillId); } for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); - final AutofillValue previousValue = previousSession - .findValueFromThisSessionOnlyLocked(autofillId); + final AutofillValue previousValue = + previousSession.findValueFromThisSessionOnlyLocked(autofillId); if (previousValue != null) { - return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()), - autofillId, previousValue); + return getSanitizedValue( + createSanitizers(previousSession.getSaveInfoLocked()), + autofillId, + previousValue); } } } @@ -1212,16 +1248,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutofillValue candidateSaveValue = state.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): current value for " + autofillId - + " is empty, using candidateSaveValue instead."); + Slog.d( + TAG, + "findValueLocked(): current value for " + + autofillId + + " is empty, using candidateSaveValue instead."); } return candidateSaveValue; } } if (value == null) { if (sDebug) { - Slog.d(TAG, "findValueLocked(): no current value for " + autofillId - + ", checking value from previous fill contexts"); + Slog.d( + TAG, + "findValueLocked(): no current value for " + + autofillId + + ", checking value from previous fill contexts"); value = getValueFromContextsLocked(autofillId); } } @@ -1231,17 +1273,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Updates values of the nodes in the context's structure so that: * - * - proper node is focused - * - autofillValue is sent back to service when it was previously autofilled - * - autofillValue is sent in the view used to force a request + * <p>- proper node is focused - autofillValue is sent back to service when it was previously + * autofilled - autofillValue is sent in the view used to force a request * * @param fillContext The context to be filled * @param flags The flags that started the session */ @GuardedBy("mLock") private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) { - final ViewNode[] nodes = fillContext - .findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); + final ViewNode[] nodes = + fillContext.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); final int numViewState = mViewStates.size(); for (int i = 0; i < numViewState; i++) { @@ -1250,7 +1291,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ViewNode node = nodes[i]; if (node == null) { if (sVerbose) { - Slog.v(TAG, + Slog.v( + TAG, "fillContextWithAllowedValuesLocked(): no node for " + viewState.id); } continue; @@ -1277,14 +1319,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Cancels the last request sent to the {@link #mRemoteFillService}. - */ + /** Cancels the last request sent to the {@link #mRemoteFillService}. */ @GuardedBy("mLock") private void cancelCurrentRequestLocked() { if (mRemoteFillService == null) { - wtf(null, "cancelCurrentRequestLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "cancelCurrentRequestLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); return; } final int canceledRequest = mRemoteFillService.cancelCurrentRequest(); @@ -1318,21 +1361,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private Optional<Integer> requestNewFillResponseLocked( @NonNull ViewState viewState, int newState, int flags) { boolean isSecondary = shouldRequestSecondaryProvider(flags); - final FillResponse existingResponse = isSecondary - ? viewState.getSecondaryResponse() : viewState.getResponse(); + final FillResponse existingResponse = + isSecondary ? viewState.getSecondaryResponse() : viewState.getResponse(); mFillRequestEventLogger.startLogForNewRequest(); mRequestCount++; mFillRequestEventLogger.maybeSetAppPackageUid(uid); mFillRequestEventLogger.maybeSetFlags(mFlags); - if(mPreviouslyFillDialogPotentiallyStarted) { + if (mPreviouslyFillDialogPotentiallyStarted) { mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_PRE_TRIGGER); } else { if ((flags & FLAG_MANUAL_REQUEST) != 0) { mFillRequestEventLogger.maybeSetRequestTriggerReason( TRIGGER_REASON_EXPLICITLY_REQUESTED); } else { - mFillRequestEventLogger.maybeSetRequestTriggerReason( - TRIGGER_REASON_NORMAL_TRIGGER); + mFillRequestEventLogger.maybeSetRequestTriggerReason(TRIGGER_REASON_NORMAL_TRIGGER); } } if (existingResponse != null) { @@ -1348,9 +1390,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionState = STATE_ACTIVE; if (mSessionFlags.mAugmentedAutofillOnly || mRemoteFillService == null) { if (sVerbose) { - Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead " - + "(mForAugmentedAutofillOnly=" + mSessionFlags.mAugmentedAutofillOnly - + ", flags=" + flags + ")"); + Slog.v( + TAG, + "requestNewFillResponse(): triggering augmented autofill instead " + + "(mForAugmentedAutofillOnly=" + + mSessionFlags.mAugmentedAutofillOnly + + ", flags=" + + flags + + ")"); } mSessionFlags.mAugmentedAutofillOnly = true; mFillRequestEventLogger.maybeSetRequestId(AUGMENTED_AUTOFILL_REQUEST_ID); @@ -1365,16 +1412,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Create a metrics log for the request final int ordinal = mRequestLogs.size() + 1; - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal); + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_REQUEST) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL, ordinal); if (flags != 0) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags); } mRequestLogs.put(requestId, log); if (sVerbose) { - Slog.v(TAG, "Requesting structure for request #" + ordinal + " ,requestId=" + requestId - + ", flags=" + flags); + Slog.v( + TAG, + "Requesting structure for request #" + + ordinal + + " ,requestId=" + + requestId + + ", flags=" + + flags); } boolean isCredmanRequested = (flags & FLAG_VIEW_REQUESTS_CREDMAN_SERVICE) != 0; mFillRequestEventLogger.maybeSetRequestId(requestId); @@ -1406,11 +1460,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // is also not focused. final RemoteInlineSuggestionRenderService remoteRenderService = mService.getRemoteInlineSuggestionRenderServiceLocked(); - if (mSessionFlags.mInlineSupportedByService && remoteRenderService != null - && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { + if (mSessionFlags.mInlineSupportedByService + && remoteRenderService != null + && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = - mAssistReceiver.newAutofillRequestLocked(viewState, - /* isInlineRequest= */ true); + mAssistReceiver.newAutofillRequestLocked( + viewState, /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final int requestIdCopy = requestId; final AutofillId focusedId = mCurrentViewId; @@ -1423,8 +1478,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState requestIdCopy, inlineSuggestionsRequestConsumer, focusedId); - RemoteCallback inlineSuggestionRendorInfoCallback = new RemoteCallback( - inlineSuggestionRendorInfoCallbackOnResultListener, mHandler); + RemoteCallback inlineSuggestionRendorInfoCallback = + new RemoteCallback( + inlineSuggestionRendorInfoCallbackOnResultListener, mHandler); remoteRenderService.getInlineSuggestionsRendererInfo( inlineSuggestionRendorInfoCallback); @@ -1465,8 +1521,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); final long identity = Binder.clearCallingIdentity(); try { - if (!ActivityTaskManager.getService().requestAutofillData(mPccAssistReceiver, - receiverExtras, mActivityToken, flags)) { + if (!ActivityTaskManager.getService() + .requestAutofillData( + mPccAssistReceiver, receiverExtras, mActivityToken, flags)) { Slog.w(TAG, "failed to request autofill data for " + mActivityToken); } } finally { @@ -1483,8 +1540,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState receiverExtras.putInt(EXTRA_REQUEST_ID, requestId); final long identity = Binder.clearCallingIdentity(); try { - if (!ActivityTaskManager.getService().requestAutofillData(mAssistReceiver, - receiverExtras, mActivityToken, flags)) { + if (!ActivityTaskManager.getService() + .requestAutofillData( + mAssistReceiver, receiverExtras, mActivityToken, flags)) { Slog.w(TAG, "failed to request autofill data for " + mActivityToken); } } finally { @@ -1495,13 +1553,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, - @NonNull Context context, @NonNull Handler handler, int userId, @NonNull Object lock, - int sessionId, int taskId, int uid, @NonNull IBinder activityToken, - @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, - @NonNull LocalLog wtfHistory, @Nullable ComponentName serviceComponentName, - @NonNull ComponentName componentName, boolean compatMode, - boolean bindInstantServiceAllowed, boolean forAugmentedAutofillOnly, int flags, + Session( + @NonNull AutofillManagerServiceImpl service, + @NonNull AutoFillUI ui, + @NonNull Context context, + @NonNull Handler handler, + int userId, + @NonNull Object lock, + int sessionId, + int taskId, + int uid, + @NonNull IBinder activityToken, + @NonNull IBinder client, + boolean hasCallback, + @NonNull LocalLog uiLatencyHistory, + @NonNull LocalLog wtfHistory, + @Nullable ComponentName serviceComponentName, + @NonNull ComponentName componentName, + boolean compatMode, + boolean bindInstantServiceAllowed, + boolean forAugmentedAutofillOnly, + int flags, @NonNull InputMethodManagerInternal inputMethodManagerInternal, boolean isPrimaryCredential) { if (sessionId < 0) { @@ -1533,22 +1605,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState primaryServiceComponentName = serviceComponentName; secondaryServiceComponentName = mCredentialAutofillService; } - Slog.v(TAG, "Primary service component name: " + primaryServiceComponentName - + ", secondary service component name: " + secondaryServiceComponentName); - - mRemoteFillService = primaryServiceComponentName == null ? null - : new RemoteFillService(context, primaryServiceComponentName, userId, this, - bindInstantServiceAllowed, mCredentialAutofillService); - mSecondaryProviderHandler = secondaryServiceComponentName == null ? null - : new SecondaryProviderHandler(context, userId, bindInstantServiceAllowed, - this::onSecondaryFillResponse, secondaryServiceComponentName, - mCredentialAutofillService); + Slog.v( + TAG, + "Primary service component name: " + + primaryServiceComponentName + + ", secondary service component name: " + + secondaryServiceComponentName); + + mRemoteFillService = + primaryServiceComponentName == null + ? null + : new RemoteFillService( + context, + primaryServiceComponentName, + userId, + this, + bindInstantServiceAllowed, + mCredentialAutofillService); + mSecondaryProviderHandler = + secondaryServiceComponentName == null + ? null + : new SecondaryProviderHandler( + context, + userId, + bindInstantServiceAllowed, + this::onSecondaryFillResponse, + secondaryServiceComponentName, + mCredentialAutofillService); mActivityToken = activityToken; mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; mWtfHistory = wtfHistory; - int displayId = LocalServices.getService(ActivityTaskManagerInternal.class) - .getDisplayId(activityToken); + int displayId = + LocalServices.getService(ActivityTaskManagerInternal.class) + .getDisplayId(activityToken); mContext = Helper.getDisplayContext(context, displayId); mComponentName = componentName; mCompatMode = compatMode; @@ -1557,8 +1647,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mStartTime = SystemClock.elapsedRealtime(); mLatencyBaseTime = mStartTime; mRequestCount = 0; - mPresentationStatsEventLogger = PresentationStatsEventLogger.createPresentationLog( - sessionId, uid, mLatencyBaseTime); + mPresentationStatsEventLogger = + PresentationStatsEventLogger.createPresentationLog( + sessionId, uid, mLatencyBaseTime); mFillRequestEventLogger = FillRequestEventLogger.forSessionId(sessionId); mFillResponseEventLogger = FillResponseEventLogger.forSessionId(sessionId); mSessionCommittedEventLogger = SessionCommittedEventLogger.forSessionId(sessionId); @@ -1574,33 +1665,39 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState setClientLocked(client); } - mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal, - userId, componentName, handler, mLock, - new InlineFillUi.InlineUiEventCallback() { - @Override - public void notifyInlineUiShown(AutofillId autofillId) { - notifyFillUiShown(autofillId); - } + mInlineSessionController = + new AutofillInlineSessionController( + inputMethodManagerInternal, + userId, + componentName, + handler, + mLock, + new InlineFillUi.InlineUiEventCallback() { + @Override + public void notifyInlineUiShown(AutofillId autofillId) { + notifyFillUiShown(autofillId); + } - @Override - public void notifyInlineUiHidden(AutofillId autofillId) { - notifyFillUiHidden(autofillId); - } - }); + @Override + public void notifyInlineUiHidden(AutofillId autofillId) { + notifyFillUiHidden(autofillId); + } + }); - mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); + mMetricsLogger.write( + newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); mLogViewEntered = false; } private ComponentName getCredentialAutofillService(Context context) { ComponentName componentName = null; - String credentialManagerAutofillCompName = context.getResources().getString( - R.string.config_defaultCredentialManagerAutofillService); + String credentialManagerAutofillCompName = + context.getResources() + .getString(R.string.config_defaultCredentialManagerAutofillService); if (credentialManagerAutofillCompName != null && !credentialManagerAutofillCompName.isEmpty()) { - componentName = ComponentName.unflattenFromString( - credentialManagerAutofillCompName); + componentName = ComponentName.unflattenFromString(credentialManagerAutofillCompName); } if (componentName == null) { Slog.w(TAG, "Invalid CredentialAutofillService"); @@ -1614,7 +1711,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @return The activity token */ @GuardedBy("mLock") - @NonNull IBinder getActivityTokenLocked() { + @NonNull + IBinder getActivityTokenLocked() { return mActivityToken; } @@ -1627,8 +1725,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#switchActivity() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#switchActivity() rejected - session: " + + id + + " destroyed"); return; } mActivityToken = newActivity; @@ -1643,17 +1744,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private void setClientLocked(@NonNull IBinder client) { unlinkClientVultureLocked(); mClient = IAutoFillManagerClient.Stub.asInterface(client); - mClientVulture = () -> { - synchronized (mLock) { - Slog.d(TAG, "handling death of " + mActivityToken + " when saving=" - + mSessionFlags.mShowingSaveUi); - if (mSessionFlags.mShowingSaveUi) { - mUi.hideFillUi(this); - } else { - mUi.destroyAll(mPendingSaveUi, this, false); - } - } - }; + mClientVulture = + () -> { + synchronized (mLock) { + Slog.d( + TAG, + "handling death of " + + mActivityToken + + " when saving=" + + mSessionFlags.mShowingSaveUi); + if (mSessionFlags.mShowingSaveUi) { + mUi.hideFillUi(this); + } else { + mUi.destroyAll(mPendingSaveUi, this, false); + } + } + }; try { mClient.asBinder().linkToDeath(mClientVulture, 0); } catch (RemoteException e) { @@ -1676,8 +1782,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override @SuppressWarnings("GuardedBy") - public void onFillRequestSuccess(int requestId, @Nullable FillResponse response, - @NonNull String servicePackageName, int requestFlags) { + public void onFillRequestSuccess( + int requestId, + @Nullable FillResponse response, + @NonNull String servicePackageName, + int requestFlags) { final AutofillId[] fieldClassificationIds; final LogMaker requestLog; @@ -1701,8 +1810,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getDetectionPreferenceForLogging()); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillRequestSuccess() rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1713,8 +1825,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // saveUi gets closed, the session will be destroyed and AutofillManager will reset // its state. Processing the fill request will result in a great chance of corrupt // state in Autofill. - Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " - + id + " is showing saveUi"); + Slog.w( + TAG, + "Call to Session#onFillRequestSuccess() rejected - session: " + + id + + " is showing saveUi"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1760,22 +1875,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final long disableDuration = response.getDisableDuration(); final boolean autofillDisabled = disableDuration > 0; if (autofillDisabled) { final int flags = response.getFlags(); final boolean disableActivityOnly = (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0; - notifyDisableAutofillToClient(disableDuration, - disableActivityOnly ? mComponentName : null); + notifyDisableAutofillToClient( + disableDuration, disableActivityOnly ? mComponentName : null); if (disableActivityOnly) { - mService.disableAutofillForActivity(mComponentName, disableDuration, - id, mCompatMode); + mService.disableAutofillForActivity( + mComponentName, disableDuration, id, mCompatMode); } else { - mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration, - id, mCompatMode); + mService.disableAutofillForApp( + mComponentName.getPackageName(), disableDuration, id, mCompatMode); } synchronized (mLock) { @@ -1786,17 +1900,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (triggerAugmentedAutofillLocked(requestFlags) != null) { mSessionFlags.mAugmentedAutofillOnly = true; if (sDebug) { - Slog.d(TAG, "Service disabled autofill for " + mComponentName - + ", but session is kept for augmented autofill only"); + Slog.d( + TAG, + "Service disabled autofill for " + + mComponentName + + ", but session is kept for augmented autofill only"); } return; } } if (sDebug) { - final StringBuilder message = new StringBuilder("Service disabled autofill for ") + final StringBuilder message = + new StringBuilder("Service disabled autofill for ") .append(mComponentName) - .append(": flags=").append(flags) + .append(": flags=") + .append(flags) .append(", duration="); TimeUtils.formatDuration(disableDuration, message); Slog.d(TAG, message.toString()); @@ -1816,8 +1935,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (requestLog != null) { - requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, - response.getDatasets() == null ? 0 : response.getDatasets().size()); + requestLog.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, + response.getDatasets() == null ? 0 : response.getDatasets().size()); if (fieldClassificationIds != null) { requestLog.addTaggedData( MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS, @@ -1840,10 +1960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - @GuardedBy("mLock") - private void processResponseLockedForPcc(@NonNull FillResponse response, - @Nullable Bundle newClientState, int flags) { + private void processResponseLockedForPcc( + @NonNull FillResponse response, @Nullable Bundle newClientState, int flags) { if (DBG) { Slog.d(TAG, "DBG: Initial response: " + response); } @@ -1851,9 +1970,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState response = getEffectiveFillResponse(response); if (isEmptyResponse(response)) { // Treat it as a null response. - processNullResponseLocked( - response != null ? response.getRequestId() : 0, - flags); + processNullResponseLocked(response != null ? response.getRequestId() : 0, flags); return; } if (DBG) { @@ -1870,9 +1987,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return ((response.getDatasets() == null || response.getDatasets().isEmpty()) && response.getAuthentication() == null && (saveInfo == null - || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) - && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) - && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) + || (ArrayUtils.isEmpty(saveInfo.getOptionalIds()) + && ArrayUtils.isEmpty(saveInfo.getRequiredIds()) + && ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) == 0))) && (ArrayUtils.isEmpty(response.getFieldClassificationIds()))); } } @@ -1884,10 +2001,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState computeDatasetsForProviderAndUpdateContainer(response, autofillProviderContainer); if (DBG) { - Slog.d(TAG, "DBG: computeDatasetsForProviderAndUpdateContainer: " - + autofillProviderContainer); + Slog.d( + TAG, + "DBG: computeDatasetsForProviderAndUpdateContainer: " + + autofillProviderContainer); } - if (!mService.isPccClassificationEnabled()) { + if (!mService.isPccClassificationEnabled()) { if (sVerbose) { Slog.v(TAG, "PCC classification is disabled"); } @@ -1897,10 +2016,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mClassificationState.mState != ClassificationState.STATE_RESPONSE || mClassificationState.mLastFieldClassificationResponse == null) { if (sVerbose) { - Slog.v(TAG, "PCC classification no last response:" - + (mClassificationState.mLastFieldClassificationResponse == null) - + " ,ineligible state=" - + (mClassificationState.mState != ClassificationState.STATE_RESPONSE)); + Slog.v( + TAG, + "PCC classification no last response:" + + (mClassificationState.mLastFieldClassificationResponse + == null) + + " ,ineligible state=" + + (mClassificationState.mState + != ClassificationState.STATE_RESPONSE)); } return createShallowCopy(response, autofillProviderContainer); } @@ -1966,8 +2089,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( (int) (fillRequestReceivedRelativeTimestamp)); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSecondaryFillResponse() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSecondaryFillResponse() rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; @@ -1981,7 +2107,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSecondaryResponses = new SparseArray<>(2); } mSecondaryResponses.put(fillResponse.getRequestId(), fillResponse); - setViewStatesLocked(fillResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false, + setViewStatesLocked( + fillResponse, + ViewState.STATE_FILLABLE, + /* clearResponse= */ false, /* isPrimary= */ false); // Updates the UI, if necessary. @@ -1997,16 +2126,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private FillResponse createShallowCopy( FillResponse response, DatasetComputationContainer container) { return FillResponse.shallowCopy( - response, - new ArrayList<>(container.mDatasets), - getEligibleSaveInfo(response)); + response, new ArrayList<>(container.mDatasets), getEligibleSaveInfo(response)); } private SaveInfo getEligibleSaveInfo(FillResponse response) { SaveInfo saveInfo = response.getSaveInfo(); - if (saveInfo == null || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds()) - || !ArrayUtils.isEmpty(saveInfo.getRequiredIds()) - || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) { + if (saveInfo == null + || (!ArrayUtils.isEmpty(saveInfo.getOptionalIds()) + || !ArrayUtils.isEmpty(saveInfo.getRequiredIds()) + || (saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0)) { return saveInfo; } synchronized (mLock) { @@ -2019,12 +2147,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState ArraySet<AutofillId> ids = new ArraySet<>(); int saveType = saveInfo.getType(); if (saveType == SaveInfo.SAVE_DATA_TYPE_GENERIC) { - for (Set<AutofillId> autofillIds: hintsToAutofillIdMap.values()) { + for (Set<AutofillId> autofillIds : hintsToAutofillIdMap.values()) { ids.addAll(autofillIds); } } else { Set<String> hints = HintsHelper.getHintsForSaveType(saveType); - for (Map.Entry<String, Set<AutofillId>> entry: hintsToAutofillIdMap.entrySet()) { + for (Map.Entry<String, Set<AutofillId>> entry : hintsToAutofillIdMap.entrySet()) { String hint = entry.getKey(); if (hints.contains(hint)) { ids.addAll(entry.getValue()); @@ -2039,9 +2167,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * A private class to hold & compute datasets to be shown - */ + /** A private class to hold & compute datasets to be shown */ private static class DatasetComputationContainer { // List of all autofill ids that have a corresponding datasets Set<AutofillId> mAutofillIds = new LinkedHashSet<>(); @@ -2103,9 +2229,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Computes datasets that are eligible to be shown based on provider detections. - * Datasets are populated in the provided container for them to be later merged with the - * PCC eligible datasets based on preference strategy. + * Computes datasets that are eligible to be shown based on provider detections. Datasets are + * populated in the provided container for them to be later merged with the PCC eligible + * datasets based on preference strategy. + * * @param response * @param container */ @@ -2210,9 +2337,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Computes datasets that are eligible to be shown based on PCC detections. - * Datasets are populated in the provided container for them to be later merged with the - * provider eligible datasets based on preference strategy. + * Computes datasets that are eligible to be shown based on PCC detections. Datasets are + * populated in the provided container for them to be later merged with the provider eligible + * datasets based on preference strategy. + * * @param response * @param container */ @@ -2267,14 +2395,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // For that, there has to be a datatype detected by PCC, and the dataset // for that datatype provided by the provider. AutofillId autofillId = dataset.getFieldIds().get(j); - if (!mClassificationState.mClassificationCombinedHintsMap - .containsKey(autofillId)) { + if (!mClassificationState.mClassificationCombinedHintsMap.containsKey( + autofillId)) { additionalEligibleAutofillIds.add(autofillId); additionalDatasetAutofillIds.add(autofillId); // For each of the field, copy over values. - copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues, - fieldPresentations, fieldDialogPresentations, - fieldInlinePresentations, fieldInlineTooltipPresentations, + copyFieldsFromDataset( + dataset, + j, + autofillId, + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, fieldFilters); } continue; @@ -2292,9 +2427,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState eligibleAutofillIds.add(autofillId); datasetAutofillIds.add(autofillId); // For each of the field, copy over values. - copyFieldsFromDataset(dataset, j, autofillId, fieldIds, fieldValues, - fieldPresentations, fieldDialogPresentations, - fieldInlinePresentations, fieldInlineTooltipPresentations, + copyFieldsFromDataset( + dataset, + j, + autofillId, + fieldIds, + fieldValues, + fieldPresentations, + fieldDialogPresentations, + fieldInlinePresentations, + fieldInlineTooltipPresentations, fieldFilters); } } @@ -2364,8 +2506,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState fieldPresentations.add(dataset.getFieldPresentation(index)); fieldDialogPresentations.add(dataset.getFieldDialogPresentation(index)); fieldInlinePresentations.add(dataset.getFieldInlinePresentation(index)); - fieldInlineTooltipPresentations.add( - dataset.getFieldInlineTooltipPresentation(index)); + fieldInlineTooltipPresentations.add(dataset.getFieldInlineTooltipPresentation(index)); fieldFilters.add(dataset.getFilter(index)); } @@ -2395,16 +2536,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState unregisterDelayedFillBroadcastLocked(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId - + ") rejected - session: " + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillRequestFailureOrTimeout(req=" + + requestId + + ") rejected - session: " + + id + + " destroyed"); mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SESSION_DESTROYED); mFillResponseEventLogger.logAndEndEvent(); return; } if (sDebug) { - Slog.d(TAG, "finishing session due to service " - + (timedOut ? "timeout" : "failure")); + Slog.d( + TAG, + "finishing session due to service " + (timedOut ? "timeout" : "failure")); } mService.resetLastResponse(); mLastFillDialogTriggerIds = null; @@ -2418,12 +2565,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int targetSdk = mService.getTargedSdkLocked(); if (targetSdk >= Build.VERSION_CODES.Q) { showMessage = false; - Slog.w(TAG, "onFillRequestFailureOrTimeout(): not showing '" + message - + "' because service's targetting API " + targetSdk); + Slog.w( + TAG, + "onFillRequestFailureOrTimeout(): not showing '" + + message + + "' because service's targeting API " + + targetSdk); } if (message != null) { - requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, - message.length()); + requestLog.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length()); } } @@ -2445,8 +2596,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); mFillResponseEventLogger.logAndEndEvent(); } - notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED, - /* autofillableIds= */ null); + notifyUnavailableToClient( + AutofillManager.STATE_UNKNOWN_FAILED, /* autofillableIds= */ null); if (showMessage) { getUiForShowing().showError(message, this); } @@ -2455,8 +2606,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestSuccess(@NonNull String servicePackageName, - @Nullable IntentSender intentSender) { + public void onSaveRequestSuccess( + @NonNull String servicePackageName, @Nullable IntentSender intentSender) { synchronized (mLock) { mSessionFlags.mShowingSaveUi = false; // Log onSaveRequest result. @@ -2464,16 +2615,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetLatencySaveFinishMillis(); mSaveEventLogger.logAndEndEvent(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSaveRequestSuccess() rejected - session: " + + id + + " destroyed"); return; } } - LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN); + LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) + .setType( + intentSender == null + ? MetricsEvent.TYPE_SUCCESS + : MetricsEvent.TYPE_OPEN); mMetricsLogger.write(log); - if (intentSender != null) { if (sDebug) Slog.d(TAG, "Starting intent sender on save()"); startIntentSenderAndFinishSession(intentSender); @@ -2485,8 +2642,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestFailure(@Nullable CharSequence message, - @NonNull String servicePackageName) { + public void onSaveRequestFailure( + @Nullable CharSequence message, @NonNull String servicePackageName) { boolean showMessage = !TextUtils.isEmpty(message); synchronized (mLock) { @@ -2495,28 +2652,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetLatencySaveFinishMillis(); mSaveEventLogger.logAndEndEvent(); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onSaveRequestFailure() rejected - session: " + + id + + " destroyed"); return; } if (showMessage) { final int targetSdk = mService.getTargedSdkLocked(); if (targetSdk >= Build.VERSION_CODES.Q) { showMessage = false; - Slog.w(TAG, "onSaveRequestFailure(): not showing '" + message - + "' because service's targetting API " + targetSdk); + Slog.w( + TAG, + "onSaveRequestFailure(): not showing '" + + message + + "' because service's targeting API " + + targetSdk); } } } final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(MetricsEvent.TYPE_FAILURE); + .setType(MetricsEvent.TYPE_FAILURE); if (message != null) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_TEXT_LEN, message.length()); } mMetricsLogger.write(log); - if (showMessage) { getUiForShowing().showError(message, this); } @@ -2525,8 +2688,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onConvertCredentialRequestSuccess(@NonNull ConvertCredentialResponse - convertCredentialResponse) { + public void onConvertCredentialRequestSuccess( + @NonNull ConvertCredentialResponse convertCredentialResponse) { Dataset dataset = convertCredentialResponse.getDataset(); Bundle clientState = convertCredentialResponse.getClientState(); if (dataset != null) { @@ -2534,15 +2697,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (clientState != null) { requestId = clientState.getInt(EXTRA_AUTOFILL_REQUEST_ID); } else { - Slog.e(TAG, "onConvertCredentialRequestSuccess(): client state is null, this " - + "would cause loss in logging."); + Slog.e( + TAG, + "onConvertCredentialRequestSuccess(): client state is null, this " + + "would cause loss in logging."); } // TODO: Add autofill related logging; consider whether to log the index - fill(requestId, /* datasetIndex=*/ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET); + fill(requestId, /* datasetIndex= */ -1, dataset, UI_TYPE_CREDMAN_BOTTOM_SHEET); } else { // TODO: Add logging to log this error case - Slog.e(TAG, "onConvertCredentialRequestSuccess(): dataset inside response is " - + "null"); + Slog.e( + TAG, + "onConvertCredentialRequestSuccess(): dataset inside response is " + "null"); } } @@ -2550,11 +2716,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Gets the {@link FillContext} for a request. * * @param requestId The id of the request - * * @return The context or {@code null} if there is no context */ @GuardedBy("mLock") - @Nullable private FillContext getFillContextByRequestIdLocked(int requestId) { + @Nullable + private FillContext getFillContextByRequestIdLocked(int requestId) { if (mContexts == null) { return null; } @@ -2582,19 +2748,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras, - int uiType) { + public void authenticate( + int requestId, int datasetIndex, IntentSender intent, Bundle extras, int uiType) { if (sDebug) { - Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex - + "; intentSender=" + intent); + Slog.d( + TAG, + "authenticate(): requestId=" + + requestId + + "; datasetIdx=" + + datasetIndex + + "; intentSender=" + + intent); } final Intent fillInIntent; synchronized (mLock) { mPresentationStatsEventLogger.maybeSetAuthenticationType( - AUTHENTICATION_TYPE_FULL_AUTHENTICATION); + AUTHENTICATION_TYPE_FULL_AUTHENTICATION); if (mDestroyed) { - Slog.w(TAG, "Call to Session#authenticate() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#authenticate() rejected - session: " + id + " destroyed"); return; } fillInIntent = createAuthFillInIntentLocked(requestId, extras); @@ -2607,10 +2780,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.setAuthenticationSelected(id, mClientState, uiType); final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); - mHandler.sendMessage(obtainMessage( - Session::startAuthentication, - this, authenticationId, intent, fillInIntent, - /* authenticateInline= */ uiType == UI_TYPE_INLINE)); + mHandler.sendMessage( + obtainMessage( + Session::startAuthentication, + this, + authenticationId, + intent, + fillInIntent, + /* authenticateInline= */ uiType == UI_TYPE_INLINE)); } // AutoFillUiCallback @@ -2618,14 +2795,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void fill(int requestId, int datasetIndex, Dataset dataset, int uiType) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#fill() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#fill() rejected - session: " + id + " destroyed"); return; } } - mHandler.sendMessage(obtainMessage( - Session::autoFill, - this, requestId, datasetIndex, dataset, true, uiType)); + mHandler.sendMessage( + obtainMessage( + Session::autoFill, this, requestId, datasetIndex, dataset, true, uiType)); } // AutoFillUiCallback @@ -2633,15 +2809,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void save() { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#save() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#save() rejected - session: " + id + " destroyed"); return; } mSaveEventLogger.maybeSetLatencySaveRequestMillis(); } - mHandler.sendMessage(obtainMessage( - AutofillManagerServiceImpl::handleSessionSave, - mService, this)); + mHandler.sendMessage( + obtainMessage(AutofillManagerServiceImpl::handleSessionSave, mService, this)); } // AutoFillUiCallback @@ -2650,13 +2824,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mSessionFlags.mShowingSaveUi = false; if (mDestroyed) { - Slog.w(TAG, "Call to Session#cancelSave() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#cancelSave() rejected - session: " + id + " destroyed"); return; } } - mHandler.sendMessage(obtainMessage( - Session::removeFromService, this)); + mHandler.sendMessage(obtainMessage(Session::removeFromService, this)); } // AutofillUiCallback @@ -2692,26 +2866,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // AutoFillUiCallback @Override - public void requestShowFillUi(AutofillId id, int width, int height, - IAutofillWindowPresenter presenter) { + public void requestShowFillUi( + AutofillId id, int width, int height, IAutofillWindowPresenter presenter) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#requestShowFillUi() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#requestShowFillUi() rejected - session: " + + id + + " destroyed"); return; } if (id.equals(mCurrentViewId)) { try { final ViewState view = mViewStates.get(id); - mClient.requestShowFillUi(this.id, id, width, height, view.getVirtualBounds(), - presenter); + mClient.requestShowFillUi( + this.id, id, width, height, view.getVirtualBounds(), presenter); } catch (RemoteException e) { Slog.e(TAG, "Error requesting to show fill UI", e); } } else { if (sDebug) { - Slog.d(TAG, "Do not show full UI on " + id + " as it is not the current view (" - + mCurrentViewId + ") anymore"); + Slog.d( + TAG, + "Do not show full UI on " + + id + + " as it is not the current view (" + + mCurrentViewId + + ") anymore"); } } } @@ -2722,8 +2904,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#dispatchUnhandledKey() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#dispatchUnhandledKey() rejected - session: " + + id + + " destroyed"); return; } if (id.equals(mCurrentViewId)) { @@ -2733,8 +2918,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Error requesting to dispatch unhandled key", e); } } else { - Slog.w(TAG, "Do not dispatch unhandled key on " + id - + " as it is not the current view (" + mCurrentViewId + ") anymore"); + Slog.w( + TAG, + "Do not dispatch unhandled key on " + + id + + " as it is not the current view (" + + mCurrentViewId + + ") anymore"); } } } @@ -2790,17 +2980,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void startIntentSender(IntentSender intentSender, Intent intent) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#startIntentSender() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#startIntentSender() rejected - session: " + + id + + " destroyed"); return; } if (intent == null) { removeFromServiceLocked(); } } - mHandler.sendMessage(obtainMessage( - Session::doStartIntentSender, - this, intentSender, intent)); + mHandler.sendMessage( + obtainMessage(Session::doStartIntentSender, this, intentSender, intent)); } // AutoFillUiCallback @@ -2865,13 +3057,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setAuthenticationResultLocked(Bundle data, int authenticationId) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#setAuthenticationResultLocked() rejected - session: " + + id + + " destroyed"); return; } if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId - + ", data=" + data); + Slog.d( + TAG, + "setAuthenticationResultLocked(): id= " + authenticationId + ", data=" + data); } final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId); if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) { @@ -2890,9 +3086,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState removeFromService(); return; } - final FillResponse authenticatedResponse = mRequestId.isSecondaryProvider(requestId) - ? mSecondaryResponses.get(requestId) - : mResponses.get(requestId); + final FillResponse authenticatedResponse = + mRequestId.isSecondaryProvider(requestId) + ? mSecondaryResponses.get(requestId) + : mResponses.get(requestId); if (authenticatedResponse == null || data == null) { Slog.w(TAG, "no authenticated response"); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2902,8 +3099,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId( - authenticationId); + final int datasetIdx = AutofillManager.getDatasetIdFromAuthenticationId(authenticationId); Dataset dataset = null; // Authenticated a dataset - reset view state regardless if we got a response or a dataset if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { @@ -2922,33 +3118,45 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mExpiredResponse = false; final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); - final GetCredentialException exception = data.getSerializable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, - GetCredentialException.class); + final GetCredentialException exception = + data.getSerializable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, + GetCredentialException.class); final Bundle newClientState = data.getBundle(AutofillManager.EXTRA_CLIENT_STATE); if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result - + ", clientState=" + newClientState + ", authenticationId=" + authenticationId); - } - if (Flags.autofillCredmanDevIntegration() && exception != null + Slog.d( + TAG, + "setAuthenticationResultLocked(): result=" + + result + + ", clientState=" + + newClientState + + ", authenticationId=" + + authenticationId); + } + if (Flags.autofillCredmanDevIntegration() + && exception != null && !exception.getType().equals(GetCredentialException.TYPE_USER_CANCELED)) { if (dataset != null && dataset.getFieldIds().size() == 1) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): result returns with" - + "Credential Manager Exception"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): result returns with" + + "Credential Manager Exception"); } AutofillId autofillId = dataset.getFieldIds().get(0); - sendCredentialManagerResponseToApp(/*response=*/ null, - (GetCredentialException) exception, autofillId); + sendCredentialManagerResponseToApp( + /* response= */ null, (GetCredentialException) exception, autofillId); } return; } if (result instanceof FillResponse) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from" - + " authentication flow"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): received FillResponse from" + + " authentication flow"); } logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED); mPresentationStatsEventLogger.maybeSetAuthenticationResult( @@ -2963,33 +3171,40 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (dataset != null && dataset.getFieldIds().size() == 1) { AutofillId autofillId = dataset.getFieldIds().get(0); if (sDebug) { - Slog.d(TAG, "Received GetCredentialResponse from authentication flow," - + "for autofillId: " + autofillId); + Slog.d( + TAG, + "Received GetCredentialResponse from authentication flow," + + "for autofillId: " + + autofillId); } - sendCredentialManagerResponseToApp(response, - /*exception=*/ null, autofillId); + sendCredentialManagerResponseToApp(response, /* exception= */ null, autofillId); } } else if (Flags.autofillCredmanIntegration()) { - Dataset datasetFromCredentialResponse = getDatasetFromCredentialResponse( - (GetCredentialResponse) result); + Dataset datasetFromCredentialResponse = + getDatasetFromCredentialResponse((GetCredentialResponse) result); if (datasetFromCredentialResponse != null) { - autoFill(requestId, datasetIdx, datasetFromCredentialResponse, - false, UI_TYPE_UNKNOWN); + autoFill( + requestId, + datasetIdx, + datasetFromCredentialResponse, + false, + UI_TYPE_UNKNOWN); } } } else if (result instanceof Dataset) { if (sDebug) { - Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from" - + " authentication flow"); + Slog.d( + TAG, + "setAuthenticationResultLocked(): received Dataset from" + + " authentication flow"); } if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); + logAuthenticationStatusLocked( + requestId, MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_SUCCESS); if (newClientState != null) { - if (sDebug) - Slog.d(TAG, "Updating client state from auth dataset"); + if (sDebug) Slog.d(TAG, "Updating client state from auth dataset"); mClientState = newClientState; } Dataset datasetFromResult = getEffectiveDatasetForAuthentication((Dataset) result); @@ -2999,10 +3214,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } autoFill(requestId, datasetIdx, datasetFromResult, false, UI_TYPE_UNKNOWN); } else { - Slog.w(TAG, "invalid index (" + datasetIdx + ") for authentication id " - + authenticationId); - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); + Slog.w( + TAG, + "invalid index (" + + datasetIdx + + ") for authentication id " + + authenticationId); + logAuthenticationStatusLocked( + requestId, MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_FAILURE); } @@ -3010,8 +3229,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (result != null) { Slog.w(TAG, "service returned invalid auth type: " + result); } - logAuthenticationStatusLocked(requestId, - MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); + logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); mPresentationStatsEventLogger.maybeSetAuthenticationResult( AUTHENTICATION_RESULT_FAILURE); processNullResponseLocked(requestId, 0); @@ -3036,8 +3254,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "DBG: authenticated effective response: " + response); } if (response == null || response.getDatasets().size() == 0) { - Log.wtf(TAG, "No datasets in fill response on authentication. response = " - + (response == null ? "null" : response.toString())); + Log.wtf( + TAG, + "No datasets in fill response on authentication. response = " + + (response == null ? "null" : response.toString())); return authenticatedDataset; } List<Dataset> datasets = response.getDatasets(); @@ -3047,8 +3267,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState for (Dataset dataset : datasets) { if (!dataset.getFieldIds().isEmpty()) { for (int i = 0; i < dataset.getFieldIds().size(); i++) { - builder.setField(dataset.getFieldIds().get(i), - new Field.Builder().setValue(dataset.getFieldValues().get(i)) + builder.setField( + dataset.getFieldIds().get(i), + new Field.Builder() + .setValue(dataset.getFieldValues().get(i)) .build()); } } @@ -3063,12 +3285,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Returns whether the dataset returned from the authentication result is ephemeral or not. - * See {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more - * information. + * Returns whether the dataset returned from the authentication result is ephemeral or not. See + * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET} for more information. */ - private static boolean isAuthResultDatasetEphemeral(@Nullable Dataset oldDataset, - @NonNull Bundle authResultData) { + private static boolean isAuthResultDatasetEphemeral( + @Nullable Dataset oldDataset, @NonNull Bundle authResultData) { if (authResultData.containsKey( AutofillManager.EXTRA_AUTHENTICATION_RESULT_EPHEMERAL_DATASET)) { return authResultData.getBoolean( @@ -3079,11 +3300,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * A dataset can potentially have multiple fields, and it's possible that some of the fields' - * has inline presentation and some don't. It's also possible that some of the fields' - * inline presentation is pinned and some isn't. So the concept of whether a dataset is - * pinned or not is ill-defined. Here we say a dataset is pinned if any of the field has a - * pinned inline presentation in the dataset. It's not ideal but hopefully it is sufficient - * for most of the cases. + * has inline presentation and some don't. It's also possible that some of the fields' inline + * presentation is pinned and some isn't. So the concept of whether a dataset is pinned or not + * is ill-defined. Here we say a dataset is pinned if any of the field has a pinned inline + * presentation in the dataset. It's not ideal but hopefully it is sufficient for most of the + * cases. */ private static boolean isPinnedDataset(@Nullable Dataset dataset) { if (dataset != null && dataset.getFieldIds() != null) { @@ -3100,16 +3321,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setAuthenticationResultForAugmentedAutofillLocked(Bundle data, int authId) { - final Dataset dataset = (data == null) ? null : - data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT, android.service.autofill.Dataset.class); + final Dataset dataset = + (data == null) + ? null + : data.getParcelable( + AutofillManager.EXTRA_AUTHENTICATION_RESULT, + android.service.autofill.Dataset.class); if (sDebug) { - Slog.d(TAG, "Auth result for augmented autofill: sessionId=" + id - + ", authId=" + authId + ", dataset=" + dataset); - } - final AutofillId fieldId = (dataset != null && dataset.getFieldIds().size() == 1) - ? dataset.getFieldIds().get(0) : null; - final AutofillValue value = (dataset != null && dataset.getFieldValues().size() == 1) - ? dataset.getFieldValues().get(0) : null; + Slog.d( + TAG, + "Auth result for augmented autofill: sessionId=" + + id + + ", authId=" + + authId + + ", dataset=" + + dataset); + } + final AutofillId fieldId = + (dataset != null && dataset.getFieldIds().size() == 1) + ? dataset.getFieldIds().get(0) + : null; + final AutofillValue value = + (dataset != null && dataset.getFieldValues().size() == 1) + ? dataset.getFieldValues().get(0) + : null; final ClipData content = (dataset != null) ? dataset.getFieldContent() : null; if (fieldId == null || (value == null && content == null)) { if (sDebug) { @@ -3154,8 +3389,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Fill the value into the field. if (sDebug) { - Slog.d(TAG, "Filling after auth: fieldId=" + fieldId + ", value=" + value - + ", content=" + content); + Slog.d( + TAG, + "Filling after auth: fieldId=" + + fieldId + + ", value=" + + value + + ", content=" + + content); } try { if (content != null) { @@ -3164,8 +3405,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClient.autofill(id, dataset.getFieldIds(), dataset.getFieldValues(), true); } } catch (RemoteException e) { - Slog.w(TAG, "Error filling after auth: fieldId=" + fieldId + ", value=" + value - + ", content=" + content, e); + Slog.w( + TAG, + "Error filling after auth: fieldId=" + + fieldId + + ", value=" + + value + + ", content=" + + content, + e); } // Clear the suggestions since the user already accepted one of them. @@ -3175,8 +3423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void setHasCallbackLocked(boolean hasIt) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#setHasCallbackLocked() rejected - session: " + + id + + " destroyed"); return; } mHasCallback = hasIt; @@ -3185,9 +3436,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") @Nullable private FillResponse getLastResponseLocked(@Nullable String logPrefixFmt) { - final String logPrefix = sDebug && logPrefixFmt != null - ? String.format(logPrefixFmt, this.id) - : null; + final String logPrefix = + sDebug && logPrefixFmt != null ? String.format(logPrefixFmt, this.id) : null; if (mContexts == null) { if (logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts"); return null; @@ -3204,16 +3454,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int lastResponseIdx = getLastResponseIndexLocked(); if (lastResponseIdx < 0) { if (logPrefix != null) { - Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses - + ", mViewStates=" + mViewStates); + Slog.w( + TAG, + logPrefix + + ": did not get last response. mResponses=" + + mResponses + + ", mViewStates=" + + mViewStates); } return null; } final FillResponse response = mResponses.valueAt(lastResponseIdx); if (sVerbose && logPrefix != null) { - Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts - + ", mViewStates=" + mViewStates); + Slog.v( + TAG, + logPrefix + + ": mResponses=" + + mResponses + + ", mContexts=" + + mContexts + + ", mViewStates=" + + mViewStates); } return response; } @@ -3232,9 +3494,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Get statistic information of save info in current session. Specifically - * 1. how many save info the current session has. - * 2. How many distinct save data types current session has. + * Get statistic information of save info in current session. Specifically 1. how many save info + * the current session has. 2. How many distinct save data types current session has. * * @return SaveInfoStats returns the above two number in a SaveInfoStats object */ @@ -3255,12 +3516,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ public void logContextCommitted() { if (sVerbose) { - Slog.v(TAG, "logContextCommitted (" + id + "): commit_reason:" + COMMIT_REASON_UNKNOWN - + " no_save_reason:" + Event.NO_SAVE_UI_REASON_NONE); - } - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, - Event.NO_SAVE_UI_REASON_NONE, - COMMIT_REASON_UNKNOWN)); + Slog.v( + TAG, + "logContextCommitted (" + + id + + "): commit_reason:" + + COMMIT_REASON_UNKNOWN + + " no_save_reason:" + + Event.NO_SAVE_UI_REASON_NONE); + } + mHandler.sendMessage( + obtainMessage( + Session::handleLogContextCommitted, + this, + Event.NO_SAVE_UI_REASON_NONE, + COMMIT_REASON_UNKNOWN)); synchronized (mLock) { logAllEventsLocked(COMMIT_REASON_UNKNOWN); } @@ -3274,16 +3544,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @param saveDialogNotShowReason The reason why a save dialog was not shown. * @param commitReason The reason why context is committed. */ - @GuardedBy("mLock") - public void logContextCommittedLocked(@NoSaveReason int saveDialogNotShowReason, - @AutofillCommitReason int commitReason) { + public void logContextCommittedLocked( + @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { if (sVerbose) { - Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason - + " no_save_reason:" + saveDialogNotShowReason); - } - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, - saveDialogNotShowReason, commitReason)); + Slog.v( + TAG, + "logContextCommittedLocked (" + + id + + "): commit_reason:" + + commitReason + + " no_save_reason:" + + saveDialogNotShowReason); + } + mHandler.sendMessage( + obtainMessage( + Session::handleLogContextCommitted, + this, + saveDialogNotShowReason, + commitReason)); mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); @@ -3295,8 +3574,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); } - private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason, - @AutofillCommitReason int commitReason) { + private void handleLogContextCommitted( + @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { final FillResponse lastResponse; synchronized (mLock) { lastResponse = getLastResponseLocked("logContextCommited(%s)"); @@ -3326,31 +3605,42 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Sets field classification scores if (userData != null && fcStrategy != null) { - logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason, - commitReason); + logFieldClassificationScore( + fcStrategy, userData, saveDialogNotShowReason, commitReason); } else { logContextCommitted(null, null, saveDialogNotShowReason, commitReason); } } - private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, + private void logContextCommitted( + @Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { synchronized (mLock) { - logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); + logContextCommittedLocked( + detectedFieldIds, + detectedFieldClassifications, + saveDialogNotShowReason, + commitReason); } } @GuardedBy("mLock") - private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, + private void logContextCommittedLocked( + @Nullable ArrayList<AutofillId> detectedFieldIds, @Nullable ArrayList<FieldClassification> detectedFieldClassifications, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { if (sVerbose) { - Slog.v(TAG, "logContextCommittedLocked (" + id + "): commit_reason:" + commitReason - + " no_save_reason:" + saveDialogNotShowReason); + Slog.v( + TAG, + "logContextCommittedLocked (" + + id + + "): commit_reason:" + + commitReason + + " no_save_reason:" + + saveDialogNotShowReason); } final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); if (lastResponse == null) return; @@ -3423,8 +3713,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue currentValue = viewState.getCurrentValue(); if (autofilledValue != null && autofilledValue.equals(currentValue)) { if (sDebug) { - Slog.d(TAG, "logContextCommitted(): ignoring changed " + viewState - + " because it has same value that was autofilled"); + Slog.d( + TAG, + "logContextCommitted(): ignoring changed " + + viewState + + " because it has same value that was autofilled"); } continue; } @@ -3442,8 +3735,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue currentValue = viewState.getCurrentValue(); if (currentValue == null) { if (sDebug) { - Slog.d(TAG, "logContextCommitted(): skipping view without current " - + "value ( " + viewState + ")"); + Slog.d( + TAG, + "logContextCommitted(): skipping view without current " + + "value ( " + + viewState + + ")"); } continue; } @@ -3455,7 +3752,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<Dataset> datasets = response.getDatasets(); if (datasets == null || datasets.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "logContextCommitted() no datasets at " + j); + Slog.v(TAG, "logContextCommitted() no datasets at " + j); } } else { for (int k = 0; k < datasets.size(); k++) { @@ -3463,8 +3760,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final String datasetId = dataset.getId(); if (datasetId == null) { if (sVerbose) { - Slog.v(TAG, "logContextCommitted() skipping idless " - + "dataset " + dataset); + Slog.v( + TAG, + "logContextCommitted() skipping idless " + + "dataset " + + dataset); } } else { final ArrayList<AutofillValue> values = @@ -3473,9 +3773,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue candidate = values.get(l); if (currentValue.equals(candidate)) { if (sDebug) { - Slog.d(TAG, "field " + viewState.id + " was " - + "manually filled with value set by " - + "dataset " + datasetId); + Slog.d( + TAG, + "field " + + viewState.id + + " was manually filled with" + + " value set by dataset " + + datasetId); } if (manuallyFilledIds == null) { manuallyFilledIds = new ArrayMap<>(); @@ -3524,10 +3828,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets, - changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, - manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, - mComponentName, mCompatMode, saveDialogNotShowReason); + mService.logContextCommittedLocked( + id, + mClientState, + mSelectedDatasetIds, + ignoredDatasets, + changedFieldIds, + changedDatasetIds, + manuallyFilledFieldIds, + manuallyFilledDatasetIds, + detectedFieldIds, + detectedFieldClassifications, + mComponentName, + mCompatMode, + saveDialogNotShowReason); mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSaveEventLogger.maybeSetSaveUiNotShownReason(saveDialogNotShowReason); @@ -3537,7 +3851,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Adds the matches to {@code detectedFieldsIds} and {@code detectedFieldClassifications} for * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ - private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy, + private void logFieldClassificationScore( + @NonNull FieldClassificationStrategy fcStrategy, @NonNull FieldClassificationUserData userData, @NoSaveReason int saveDialogNotShowReason, @AutofillCommitReason int commitReason) { @@ -3555,16 +3870,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) { final int valuesLength = userValues == null ? -1 : userValues.length; final int idsLength = categoryIds == null ? -1 : categoryIds.length; - Slog.w(TAG, "setScores(): user data mismatch: values.length = " - + valuesLength + ", ids.length = " + idsLength); + Slog.w( + TAG, + "setScores(): user data mismatch: values.length = " + + valuesLength + + ", ids.length = " + + idsLength); return; } final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize(); final ArrayList<AutofillId> detectedFieldIds = new ArrayList<>(maxFieldsSize); - final ArrayList<FieldClassification> detectedFieldClassifications = new ArrayList<>( - maxFieldsSize); + final ArrayList<FieldClassification> detectedFieldClassifications = + new ArrayList<>(maxFieldsSize); final Collection<ViewState> viewStates; synchronized (mLock) { @@ -3583,33 +3902,49 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Then use the results, asynchronously - final RemoteCallback callback = new RemoteCallback( - new LogFieldClassificationScoreOnResultListener( - this, - saveDialogNotShowReason, - commitReason, - viewsSize, - autofillIds, - userValues, - categoryIds, - detectedFieldIds, - detectedFieldClassifications)); - - fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, - defaultAlgorithm, defaultArgs, algorithms, args); - } - - void handleLogFieldClassificationScore(@Nullable Bundle result, int saveDialogNotShowReason, - int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues, - String[] categoryIds, ArrayList<AutofillId> detectedFieldIds, + final RemoteCallback callback = + new RemoteCallback( + new LogFieldClassificationScoreOnResultListener( + this, + saveDialogNotShowReason, + commitReason, + viewsSize, + autofillIds, + userValues, + categoryIds, + detectedFieldIds, + detectedFieldClassifications)); + + fcStrategy.calculateScores( + callback, + currentValues, + userValues, + categoryIds, + defaultAlgorithm, + defaultArgs, + algorithms, + args); + } + + void handleLogFieldClassificationScore( + @Nullable Bundle result, + int saveDialogNotShowReason, + int commitReason, + int viewsSize, + AutofillId[] autofillIds, + String[] userValues, + String[] categoryIds, + ArrayList<AutofillId> detectedFieldIds, ArrayList<FieldClassification> detectedFieldClassifications) { if (result == null) { if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); logContextCommitted(null, null, saveDialogNotShowReason, commitReason); return; } - final Scores scores = result.getParcelable(EXTRA_SCORES, - android.service.autofill.AutofillFieldClassificationService.Scores.class); + final Scores scores = + result.getParcelable( + EXTRA_SCORES, + android.service.autofill.AutofillFieldClassificationService.Scores.class); if (scores == null) { Slog.w(TAG, "No field classification score on " + result); return; @@ -3633,14 +3968,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Float currentScore = scoresByField.get(categoryId); if (currentScore != null && currentScore > score) { if (sVerbose) { - Slog.v(TAG, "skipping score " + score - + " because it's less than " + currentScore); + Slog.v( + TAG, + "skipping score " + + score + + " because it's less than " + + currentScore); } continue; } if (sVerbose) { - Slog.v(TAG, "adding score " + score + " at index " + j + " and id " - + autofillId); + Slog.v( + TAG, + "adding score " + + score + + " at index " + + j + + " and id " + + autofillId); } scoresByField.put(categoryId, score); } else if (sVerbose) { @@ -3666,13 +4011,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e); return; } - logContextCommitted(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); + logContextCommitted( + detectedFieldIds, + detectedFieldClassifications, + saveDialogNotShowReason, + commitReason); } /** - * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} - * when necessary. + * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} when + * necessary. * * <p>Note: It is necessary to call logContextCommitted() first before calling this method. */ @@ -3689,11 +4037,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @NonNull public SaveResult showSaveLocked() { if (mDestroyed) { - Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#showSaveLocked() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } mSessionState = STATE_FINISHED; @@ -3705,12 +4056,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ if (mSessionFlags.mScreenHasCredmanField) { if (sVerbose) { - Slog.v(TAG, "Call to Session#showSaveLocked() rejected - " - + "there is credman field in screen"); + Slog.v( + TAG, + "Call to Session#showSaveLocked() rejected - " + + "there is credman field in screen"); } mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SCREEN_HAS_CREDMAN_FIELD); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NONE); } @@ -3728,7 +4083,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NO_SAVE_INFO); } @@ -3737,7 +4094,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG); } @@ -3774,12 +4133,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Some apps clear the form before navigating to other activities. // If current value is empty, consider fall back to last cached // non-empty result first. - final AutofillValue candidateSaveValue = - viewState.getCandidateSaveValue(); + final AutofillValue candidateSaveValue = viewState.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "current value is empty, using cached last non-empty " - + "value instead"); + Slog.v( + TAG, + "current value is empty, using cached last non-empty " + + "value instead"); } value = candidateSaveValue; } else { @@ -3788,8 +4148,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue initialValue = getValueFromContextsLocked(id); if (initialValue != null) { if (sDebug) { - Slog.d(TAG, "Value of required field " + id + " didn't change; " - + "using initial value (" + initialValue + ") instead"); + Slog.d( + TAG, + "Value of required field " + + id + + " didn't change; " + + "using initial value (" + + initialValue + + ") instead"); } value = initialValue; } else { @@ -3821,8 +4187,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue initialValue = getValueFromContextsLocked(id); if (initialValue != null && initialValue.equals(value)) { if (sDebug) { - Slog.d(TAG, "id " + id + " is part of dataset but initial value " - + "didn't change: " + value); + Slog.d( + TAG, + "id " + + id + + " is part of dataset but initial value " + + "didn't change: " + + value); } changed = false; } else { @@ -3833,8 +4204,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (changed) { if (sDebug) { - Slog.d(TAG, "found a change on required " + id + ": " + filledValue - + " => " + value); + Slog.d( + TAG, + "found a change on required " + + id + + ": " + + filledValue + + " => " + + value); } atLeastOneChanged = true; } @@ -3844,8 +4221,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId[] optionalIds = saveInfo.getOptionalIds(); if (sVerbose) { - Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: " - + (optionalIds != null)); + Slog.v( + TAG, + "allRequiredAreNotEmpty: " + + allRequiredAreNotEmpty + + " hasOptional: " + + (optionalIds != null)); } int saveDialogNotShowReason; if (!allRequiredAreNotEmpty) { @@ -3859,7 +4240,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // - if at least one required id changed but it was not part of a filled dataset, we // need to check if an optional id is part of a filled datased (in which case we show // Update instead of Save) - if (optionalIds!= null && (!atLeastOneChanged || !isUpdate)) { + if (optionalIds != null && (!atLeastOneChanged || !isUpdate)) { // No change on required ids yet, look for changes on optional ids. for (int i = 0; i < optionalIds.length; i++) { final AutofillId id = optionalIds[i]; @@ -3879,8 +4260,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState viewState.getCandidateSaveValue(); if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { if (sVerbose) { - Slog.v(TAG, "current value is empty, using cached last " - + "non-empty value instead"); + Slog.v( + TAG, + "current value is empty, using cached last " + + "non-empty value instead"); } currentValue = candidateSaveValue; } @@ -3897,8 +4280,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue filledValue = viewState.getAutofilledValue(); if (value != null && !value.equals(filledValue)) { if (sDebug) { - Slog.d(TAG, "found a change on optional " + id + ": " + filledValue - + " => " + value); + Slog.d( + TAG, + "found a change on optional " + + id + + ": " + + filledValue + + " => " + + value); } if (filledValue != null) { isUpdate = true; @@ -3907,12 +4296,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } atLeastOneChanged = true; } - } else { + } else { // Update current values cache based on initial value final AutofillValue initialValue = getValueFromContextsLocked(id); if (sDebug) { - Slog.d(TAG, "no current value for " + id + "; initial value is " - + initialValue); + Slog.d( + TAG, + "no current value for " + + id + + "; initial value is " + + initialValue); } if (initialValue != null) { currentValues.put(id, initialValue); @@ -3935,17 +4328,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { isValid = validator.isValid(this); if (sDebug) Slog.d(TAG, validator + " returned " + isValid); - log.setType(isValid - ? MetricsEvent.TYPE_SUCCESS - : MetricsEvent.TYPE_DISMISS); + log.setType( + isValid ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_DISMISS); } catch (Exception e) { Slog.e(TAG, "Not showing save UI because validation failed:", e); log.setType(MetricsEvent.TYPE_FAILURE); mMetricsLogger.write(log); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_FIELD_VALIDATION_FAILED); + NO_SAVE_REASON_FIELD_VALIDATION_FAILED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED); } @@ -3953,9 +4347,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (!isValid) { Slog.i(TAG, "not showing save UI because fields failed validation"); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_FIELD_VALIDATION_FAILED); + NO_SAVE_REASON_FIELD_VALIDATION_FAILED); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED); } } @@ -3964,15 +4360,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // content. final List<Dataset> datasets = response.getDatasets(); if (datasets != null) { - datasets_loop: for (int i = 0; i < datasets.size(); i++) { + datasets_loop: + for (int i = 0; i < datasets.size(); i++) { final Dataset dataset = datasets.get(i); final ArrayMap<AutofillId, AutofillValue> datasetValues = Helper.getFields(dataset); if (sVerbose) { - Slog.v(TAG, "Checking if saved fields match contents of dataset #" + i - + ": " + dataset + "; savableIds=" + savableIds); + Slog.v( + TAG, + "Checking if saved fields match contents of dataset #" + + i + + ": " + + dataset + + "; savableIds=" + + savableIds); } - savable_ids_loop: for (int j = 0; j < savableIds.size(); j++) { + savable_ids_loop: + for (int j = 0; j < savableIds.size(); j++) { final AutofillId id = savableIds.valueAt(j); final AutofillValue currentValue = currentValues.get(id); if (currentValue == null) { @@ -3984,20 +4388,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillValue datasetValue = datasetValues.get(id); if (!currentValue.equals(datasetValue)) { if (sDebug) { - Slog.d(TAG, "found a dataset change on id " + id + ": from " - + datasetValue + " to " + currentValue); + Slog.d( + TAG, + "found a dataset change on id " + + id + + ": from " + + datasetValue + + " to " + + currentValue); } continue datasets_loop; } if (sVerbose) Slog.v(TAG, "no dataset changes for id " + id); } if (sDebug) { - Slog.d(TAG, "ignoring Save UI because all fields match contents of " - + "dataset #" + i + ": " + dataset); + Slog.d( + TAG, + "ignoring Save UI because all fields match contents of " + + "dataset #" + + i + + ": " + + dataset); } mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_DATASET_MATCH); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_DATASET_MATCH); } } @@ -4015,13 +4432,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState wtf(null, "showSaveLocked(): no service label or icon"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); mSaveEventLogger.logAndEndEvent(); - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + return new SaveResult( + /* logSaveShown= */ false, + /* removeSession= */ true, Event.NO_SAVE_UI_REASON_NONE); } - getUiForShowing().showSaveUi(serviceLabel, serviceIcon, - mService.getServicePackageName(), saveInfo, this, - mComponentName, this, mContext, mPendingSaveUi, isUpdate, mCompatMode, - response.getShowSaveDialogIcon(), mSaveEventLogger); + getUiForShowing() + .showSaveUi( + serviceLabel, + serviceIcon, + mService.getServicePackageName(), + saveInfo, + this, + mComponentName, + this, + mContext, + mPendingSaveUi, + isUpdate, + mCompatMode, + response.getShowSaveDialogIcon(), + mSaveEventLogger); if (client != null) { try { client.setSaveUiState(id, true); @@ -4031,21 +4461,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mSessionFlags.mShowingSaveUi = true; if (sDebug) { - Slog.d(TAG, "Good news, everyone! All checks passed, show save UI for " - + id + "!"); + Slog.d( + TAG, + "Good news, everyone! All checks passed, show save UI for " + id + "!"); } - return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, + return new SaveResult( + /* logSaveShown= */ true, + /* removeSession= */ false, Event.NO_SAVE_UI_REASON_NONE); } } // Nothing changed... if (sDebug) { - Slog.d(TAG, "showSaveLocked(" + id +"): with no changes, comes no responsibilities." - + "allRequiredAreNotNull=" + allRequiredAreNotEmpty - + ", atLeastOneChanged=" + atLeastOneChanged); + Slog.d( + TAG, + "showSaveLocked(" + + id + + "): with no changes, comes no responsibilities." + + "allRequiredAreNotNull=" + + allRequiredAreNotEmpty + + ", atLeastOneChanged=" + + atLeastOneChanged); } - return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, - saveDialogNotShowReason); + return new SaveResult( + /* logSaveShown= */ false, /* removeSession= */ true, saveDialogNotShowReason); } private void logSaveShown() { @@ -4078,25 +4517,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return sanitized; } - /** - * Returns whether the session is currently showing the save UI - */ + /** Returns whether the session is currently showing the save UI */ @GuardedBy("mLock") boolean isSaveUiShowingLocked() { return mSessionFlags.mShowingSaveUi; } - /** - * Gets the latest non-empty value for the given id in the autofill contexts. - */ + /** Gets the latest non-empty value for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private ViewNode getViewNodeFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null) { return node; } @@ -4104,22 +4539,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - /** - * Gets the latest non-empty value for the given id in the autofill contexts. - */ + /** Gets the latest non-empty value for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null) { final AutofillValue value = node.getAutofillValue(); if (sDebug) { - Slog.d(TAG, "getValueFromContexts(" + this.id + "/" + autofillId + ") at " - + i + ": " + value); + Slog.d( + TAG, + "getValueFromContexts(" + + this.id + + "/" + + autofillId + + ") at " + + i + + ": " + + value); } if (value != null && !value.isEmpty()) { return value; @@ -4129,17 +4570,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - /** - * Gets the latest autofill options for the given id in the autofill contexts. - */ + /** Gets the latest autofill options for the given id in the autofill contexts. */ @GuardedBy("mLock") @Nullable private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), - autofillId); + final ViewNode node = + Helper.findViewNodeByAutofillId(context.getStructure(), autofillId); if (node != null && node.getAutofillOptions() != null) { return node.getAutofillOptions(); } @@ -4160,7 +4599,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = mContexts.get(contextNum); final ViewNode[] nodes = - context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); + context.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked()); if (sVerbose) Slog.v(TAG, "updateValuesForSaveLocked(): updating " + context); @@ -4191,8 +4630,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sanitizedValue != null) { node.updateAutofillValue(sanitizedValue); } else if (sDebug) { - Slog.d(TAG, "updateValuesForSaveLocked(): not updating field " + id - + " because it failed sanitization"); + Slog.d( + TAG, + "updateValuesForSaveLocked(): not updating field " + + id + + " because it failed sanitization"); } } @@ -4200,28 +4642,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState context.getStructure().sanitizeForParceling(false); if (sVerbose) { - Slog.v(TAG, "updateValuesForSaveLocked(): dumping structure of " + context - + " before calling service.save()"); + Slog.v( + TAG, + "updateValuesForSaveLocked(): dumping structure of " + + context + + " before calling service.save()"); context.getStructure().dump(false); } } } - /** - * Calls service when user requested save. - */ + /** Calls service when user requested save. */ @GuardedBy("mLock") void callSaveLocked() { if (mDestroyed) { - Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#callSaveLocked() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetIsSaved(false); mSaveEventLogger.logAndEndEvent(); return; } if (mRemoteFillService == null) { - wtf(null, "callSaveLocked() called without a remote service. " - + "mForAugmentedAutofillOnly: %s", mSessionFlags.mAugmentedAutofillOnly); + wtf( + null, + "callSaveLocked() called without a remote service. " + + "mForAugmentedAutofillOnly: %s", + mSessionFlags.mAugmentedAutofillOnly); mSaveEventLogger.maybeSetIsSaved(false); mSaveEventLogger.logAndEndEvent(); return; @@ -4241,7 +4688,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Remove pending fill requests as the session is finished. cancelCurrentRequestLocked(); - final ArrayList<FillContext> contexts = mergePreviousSessionLocked( /* forSave= */ true); + final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ true); FieldClassificationResponse fieldClassificationResponse = mClassificationState.mLastFieldClassificationResponse; @@ -4251,8 +4698,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mClientState == null) { mClientState = new Bundle(); } - mClientState.putParcelableArrayList(EXTRA_KEY_DETECTIONS, new ArrayList<>( - fieldClassificationResponse.getClassifications())); + mClientState.putParcelableArrayList( + EXTRA_KEY_DETECTIONS, + new ArrayList<>(fieldClassificationResponse.getClassifications())); } final SaveRequest saveRequest = new SaveRequest(contexts, mClientState, mSelectedDatasetIds); @@ -4270,11 +4718,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * from previous sessions that were asked by the service to be delayed (if any). * * <p>As a side-effect: + * * <ul> - * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- - * {@code null} client state from previous sessions. + * <li>If the current {@link #mClientState} is {@code null}, sets it with the last non- {@code + * null} client state from previous sessions. * <li>When {@code forSave} is {@code true}, calls {@link #updateValuesForSaveLocked()} in the - * previous sessions. + * previous sessions. * </ul> */ @NonNull @@ -4283,30 +4732,51 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts; if (previousSessions != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): Merging the content of " - + previousSessions.size() + " sessions for task " + taskId); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): Merging the content of " + + previousSessions.size() + + " sessions for task " + + taskId); } contexts = new ArrayList<>(); for (int i = 0; i < previousSessions.size(); i++) { final Session previousSession = previousSessions.get(i); final ArrayList<FillContext> previousContexts = previousSession.mContexts; if (previousContexts == null) { - Slog.w(TAG, "mergeSessions(" + this.id + "): Not merging null contexts from " - + previousSession.id); + Slog.w( + TAG, + "mergeSessions(" + + this.id + + "): Not merging null contexts from " + + previousSession.id); continue; } if (forSave) { previousSession.updateValuesForSaveLocked(); } if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): adding " + previousContexts.size() - + " context from previous session #" + previousSession.id); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): adding " + + previousContexts.size() + + " context from previous session #" + + previousSession.id); } contexts.addAll(previousContexts); if (mClientState == null && previousSession.mClientState != null) { if (sDebug) { - Slog.d(TAG, "mergeSessions(" + this.id + "): setting client state from " - + "previous session" + previousSession.id); + Slog.d( + TAG, + "mergeSessions(" + + this.id + + "): setting client state from " + + "previous session" + + previousSession.id); } mClientState = previousSession.mClientState; } @@ -4351,8 +4821,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // If it's not, then check if it should start a partition. if (shouldStartNewPartitionLocked(id, flags)) { if (sDebug) { - Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": " - + viewState.getStateAsString()); + Slog.d( + TAG, + "Starting partition or augmented request for view id " + + id + + ": " + + viewState.getStateAsString()); } // Fix to always let standard autofill start. // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as @@ -4363,8 +4837,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (sVerbose) { - Slog.v(TAG, "Not starting new partition for view " + id + ": " - + viewState.getStateAsString()); + Slog.v( + TAG, + "Not starting new partition for view " + + id + + ": " + + viewState.getStateAsString()); } return Optional.empty(); } @@ -4373,17 +4851,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Determines if a new partition should be started for an id. * * @param id The id of the view that is entered - * * @return {@code true} if a new partition should be started */ @GuardedBy("mLock") private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id, int flags) { final ViewState currentView = mViewStates.get(id); - SparseArray<FillResponse> responses = shouldRequestSecondaryProvider(flags) - ? mSecondaryResponses : mResponses; + SparseArray<FillResponse> responses = + shouldRequestSecondaryProvider(flags) ? mSecondaryResponses : mResponses; if (responses == null) { - return currentView != null && (currentView.getState() - & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0; + return currentView != null + && (currentView.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) + == 0; } if (mSessionFlags.mExpiredResponse) { @@ -4395,8 +4873,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int numResponses = responses.size(); if (numResponses >= AutofillManagerService.getPartitionMaxCount()) { - Slog.e(TAG, "Not starting a new partition on " + id + " because session " + this.id - + " reached maximum of " + AutofillManagerService.getPartitionMaxCount()); + Slog.e( + TAG, + "Not starting a new partition on " + + id + + " because session " + + this.id + + " reached maximum of " + + AutofillManagerService.getPartitionMaxCount()); return false; } @@ -4437,8 +4921,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } boolean shouldRequestSecondaryProvider(int flags) { - if (!mService.isAutofillCredmanIntegrationEnabled() - || mSecondaryProviderHandler == null) { + if (!mService.isAutofillCredmanIntegrationEnabled() || mSecondaryProviderHandler == null) { return false; } if (mIsPrimaryCredential) { @@ -4452,8 +4935,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // 'Session.this.mLock', which is the same as mLock. @SuppressWarnings("GuardedBy") @GuardedBy("mLock") - void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action, - int flags) { + void updateLocked( + AutofillId id, Rect virtualBounds, AutofillValue value, int action, int flags) { if (mDestroyed) { Slog.w(TAG, "updateLocked(" + id + "): rejected - session: destroyed"); return; @@ -4464,7 +4947,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "updateLocked(" + id + "): Set the response has expired."); } mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists( - NOT_SHOWN_REASON_VIEW_CHANGED); + NOT_SHOWN_REASON_VIEW_CHANGED); mPresentationStatsEventLogger.logAndEndEvent("ACTION_RESPONSE_EXPIRED"); return; } @@ -4474,23 +4957,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) { Slog.v( TAG, - "updateLocked(" + id + "): " - + "id=" + this.id - + ", action=" + actionAsString(action) - + ", flags=" + flags - + ", mCurrentViewId=" + mCurrentViewId - + ", mExpiredResponse=" + mSessionFlags.mExpiredResponse - + ", viewState=" + viewState); + "updateLocked(" + + id + + "): " + + "id=" + + this.id + + ", action=" + + actionAsString(action) + + ", flags=" + + flags + + ", mCurrentViewId=" + + mCurrentViewId + + ", mExpiredResponse=" + + mSessionFlags.mExpiredResponse + + ", viewState=" + + viewState); } if (viewState == null) { - if (action == ACTION_START_SESSION || action == ACTION_VALUE_CHANGED + if (action == ACTION_START_SESSION + || action == ACTION_VALUE_CHANGED || action == ACTION_VIEW_ENTERED) { if (sVerbose) Slog.v(TAG, "Creating viewState for " + id); boolean isIgnored = isIgnoredLocked(id); - viewState = new ViewState(id, this, - isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL, - mIsPrimaryCredential); + viewState = + new ViewState( + id, + this, + isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL, + mIsPrimaryCredential); mViewStates.put(id, viewState); // TODO(b/73648631): for optimization purposes, should also ignore if change is @@ -4521,7 +5016,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionFlags.mScreenHasCredmanField = true; } - switch(action) { + switch (action) { case ACTION_START_SESSION: // View is triggering autofill. mCurrentViewId = viewState.id; @@ -4545,14 +5040,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState case ACTION_VALUE_CHANGED: if (mCompatMode && (viewState.getState() & ViewState.STATE_URL_BAR) != 0) { // Must cancel the session if the value of the URL bar changed - final String currentUrl = mUrlBar == null ? null - : mUrlBar.getText().toString().trim(); + final String currentUrl = + mUrlBar == null ? null : mUrlBar.getText().toString().trim(); if (currentUrl == null) { // Validation check - shouldn't happen. wtf(null, "URL bar value changed, but current value is null"); return; } - if (value == null || ! value.isText()) { + if (value == null || !value.isText()) { // Validation check - shouldn't happen. wtf(null, "URL bar value changed to null or non-text: %s", value); return; @@ -4567,8 +5062,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // are finished, as the URL bar changed callback is usually called before // the virtual views become invisible. if (sDebug) { - Slog.d(TAG, "Ignoring change on URL because session will finish when " - + "views are gone"); + Slog.d( + TAG, + "Ignoring change on URL because session will finish when " + + "views are gone"); } return; } @@ -4598,8 +5095,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // isSameViewEntered has some limitations, where it isn't considered same view when // autofill suggestions pop up, user selects, and the focus lands back on the view. // isSameViewAgain tries to overcome that situation. - final boolean isSameViewAgain = isSameViewEntered - || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId); + final boolean isSameViewAgain = + isSameViewEntered + || Objects.equals(mCurrentViewId, mPreviousNonNullEnteredViewId); if (mCurrentViewId != null) { mPreviousNonNullEnteredViewId = mCurrentViewId; } @@ -4655,8 +5153,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Trigger augmented autofill if applicable if ((flags & FLAG_MANUAL_REQUEST) == 0) { // Not a manual request - if (mAugmentedAutofillableIds != null && mAugmentedAutofillableIds.contains( - id)) { + if (mAugmentedAutofillableIds != null + && mAugmentedAutofillableIds.contains(id)) { // Regular autofill handled the view and returned null response, but it // triggered augmented autofill if (!isSameViewEntered) { @@ -4664,16 +5162,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState triggerAugmentedAutofillLocked(flags); } else { if (sDebug) { - Slog.d(TAG, "skip augmented autofill for same view: " - + "same view entered"); + Slog.d( + TAG, + "skip augmented autofill for same view: " + + "same view entered"); } } return; } else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) { // Regular autofill is disabled. if (sDebug) { - Slog.d(TAG, "skip augmented autofill for same view: " - + "standard autofill disabled."); + Slog.d( + TAG, + "skip augmented autofill for same view: " + + "standard autofill disabled."); } return; } @@ -4717,8 +5219,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // on the IME side if it arrives before the input view is finished on the IME. mInlineSessionController.resetInlineFillUiLocked(); - if ((viewState.getState() & - ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) != 0) { + if ((viewState.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) + != 0) { // View was exited before Inline Request sent back, do not set it to // null yet to let onHandleAssistData finish processing } else { @@ -4728,7 +5230,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // It's not necessary that there's no more presentation for this view. It could // be that the user chose some suggestion, in which case, view exits. mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); + NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED); } break; default: @@ -4737,8 +5239,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logPresentationStatsOnViewEnteredLocked(FillResponse response, - boolean isCredmanRequested) { + private void logPresentationStatsOnViewEnteredLocked( + FillResponse response, boolean isCredmanRequested) { mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested); mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( mFieldClassificationIdSnapshot); @@ -4754,16 +5256,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void hideAugmentedAutofillLocked(@NonNull ViewState viewState) { - if ((viewState.getState() - & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { + if ((viewState.getState() & ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL) != 0) { viewState.resetState(ViewState.STATE_TRIGGERED_AUGMENTED_AUTOFILL); cancelAugmentedAutofillLocked(); } } - /** - * Checks whether a view should be ignored. - */ + /** Checks whether a view should be ignored. */ @GuardedBy("mLock") private boolean isIgnoredLocked(AutofillId id) { // Always check the latest response only @@ -4782,22 +5281,30 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && getSaveInfoLocked() != null) { final int length = viewState.getCurrentValue().getTextValue().length(); if (sDebug) { - Slog.d(TAG, "updateLocked(" + id + "): resetting value that was " - + length + " chars long"); - } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); + Slog.d( + TAG, + "updateLocked(" + + id + + "): resetting value that was " + + length + + " chars long"); + } + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); mMetricsLogger.write(log); } } @GuardedBy("mLock") - private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value, - ViewState viewState, int flags) { + private void updateViewStateAndUiOnValueChangedLocked( + AutofillId id, AutofillValue value, ViewState viewState, int flags) { // Cache the last non-empty value for save purpose. Some apps clear the form before // navigating to other activities. - if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty()) - && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText() + if (mIgnoreViewStateResetToEmpty + && (value == null || value.isEmpty()) + && viewState.getCurrentValue() != null + && viewState.getCurrentValue().isText() && viewState.getCurrentValue().getTextValue() != null && viewState.getCurrentValue().getTextValue().length() > 1) { if (sVerbose) { @@ -4875,8 +5382,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * indicate the IME attempting to probe the potentially sensitive content of inline suggestions. */ @GuardedBy("mLock") - private void updateFilteringStateOnValueChangedLocked(@Nullable String newTextValue, - ViewState viewState) { + private void updateFilteringStateOnValueChangedLocked( + @Nullable String newTextValue, ViewState viewState) { if (newTextValue == null) { // Don't just return here, otherwise the IME can circumvent this logic using non-text // values. @@ -4901,18 +5408,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @Override - public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId, - @Nullable AutofillValue value, int flags) { + public void onFillReady( + @NonNull FillResponse response, + @NonNull AutofillId filledId, + @Nullable AutofillValue value, + int flags) { synchronized (mLock) { mPresentationStatsEventLogger.maybeSetFieldClassificationRequestId( mFieldClassificationIdSnapshot); if (mDestroyed) { - Slog.w(TAG, "Call to Session#onFillReady() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#onFillReady() rejected - session: " + id + " destroyed"); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_SESSION_DESTROYED); mSaveEventLogger.logAndEndEvent(); mPresentationStatsEventLogger.maybeSetNoPresentationEventReason( - NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY); + NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY); mPresentationStatsEventLogger.logAndEndEvent("on fill ready"); return; } @@ -4954,7 +5465,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else { setFillDialogDisabled(); } - } if (response.supportsInlineSuggestions()) { @@ -4971,10 +5481,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - getUiForShowing().showFillUi(filledId, response, filterText, - mService.getServicePackageName(), mComponentName, - serviceLabel, serviceIcon, this, mContext, id, mCompatMode, - mService.getMaster().getMaxInputLengthForAutofill()); + getUiForShowing() + .showFillUi( + filledId, + response, + filterText, + mService.getServicePackageName(), + mComponentName, + serviceLabel, + serviceIcon, + this, + mContext, + id, + mCompatMode, + mService.getMaster().getMaxInputLengthForAutofill()); synchronized (mLock) { if (mUiShownTime == 0) { @@ -4983,21 +5503,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final long duration = mUiShownTime - mStartTime; if (sDebug) { - final StringBuilder msg = new StringBuilder("1st UI for ") - .append(mActivityToken) - .append(" shown in "); + final StringBuilder msg = + new StringBuilder("1st UI for ") + .append(mActivityToken) + .append(" shown in "); TimeUtils.formatDuration(duration, msg); Slog.d(TAG, msg.toString()); } - final StringBuilder historyLog = new StringBuilder("id=").append(id) - .append(" app=").append(mActivityToken) - .append(" svc=").append(mService.getServicePackageName()) - .append(" latency="); + final StringBuilder historyLog = + new StringBuilder("id=") + .append(id) + .append(" app=") + .append(mActivityToken) + .append(" svc=") + .append(mService.getServicePackageName()) + .append(" latency="); TimeUtils.formatDuration(duration, historyLog); mUiLatencyHistory.log(historyLog.toString()); - addTaggedDataToRequestLogLocked(response.getRequestId(), - MetricsEvent.FIELD_AUTOFILL_DURATION, duration); + addTaggedDataToRequestLogLocked( + response.getRequestId(), MetricsEvent.FIELD_AUTOFILL_DURATION, duration); } } } @@ -5052,8 +5577,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private boolean requestShowFillDialog(FillResponse response, - AutofillId filledId, String filterText, int flags) { + private boolean requestShowFillDialog( + FillResponse response, AutofillId filledId, String filterText, int flags) { if (!isFillDialogUiEnabled()) { // Unsupported fill dialog UI if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled"); @@ -5079,7 +5604,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed."); return false; } - } Drawable serviceIcon = null; @@ -5087,21 +5611,31 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState serviceIcon = getServiceIcon(response); } - getUiForShowing().showFillDialog(filledId, response, filterText, - mService.getServicePackageName(), mComponentName, serviceIcon, this, - id, mCompatMode, mPresentationStatsEventLogger, mLock); + getUiForShowing() + .showFillDialog( + filledId, + response, + filterText, + mService.getServicePackageName(), + mComponentName, + serviceIcon, + this, + id, + mCompatMode, + mPresentationStatsEventLogger, + mLock); return true; } /** - * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able - * to be fetched, use the default provider icon instead + * Get the custom icon that was passed through FillResponse. If the custom icon wasn't able to + * be fetched, use the default provider icon instead * * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise */ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's - // actually the same object as mLock. - // TODO: Expose mService.mLock or redesign instead. + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. @GuardedBy("mLock") private Drawable getServiceIcon(FillResponse response) { Drawable serviceIcon = null; @@ -5130,14 +5664,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Get the custom label that was passed through FillResponse. If the custom label - * wasn't able to be fetched, use the default provider icon instead + * Get the custom label that was passed through FillResponse. If the custom label wasn't able to + * be fetched, use the default provider icon instead * * @return Drawable of the provider icon, if it was able to be fetched. Null otherwise */ @SuppressWarnings("GuardedBy") // ErrorProne says we need to use mService.mLock, but it's - // actually the same object as mLock. - // TODO: Expose mService.mLock or redesign instead. + // actually the same object as mLock. + // TODO: Expose mService.mLock or redesign instead. @GuardedBy("mLock") private CharSequence getServiceLabel(FillResponse response) { CharSequence serviceLabel = null; @@ -5167,11 +5701,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return serviceLabel; } - /** - * Returns whether we made a request to show inline suggestions. - */ - private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response, - @Nullable String filterText) { + /** Returns whether we made a request to show inline suggestions. */ + private boolean requestShowInlineSuggestionsLocked( + @NonNull FillResponse response, @Nullable String filterText) { if (mCurrentViewId == null) { Log.w(TAG, "requestShowInlineSuggestionsLocked(): no view currently focused"); return false; @@ -5199,89 +5731,122 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } final InlineFillUi.InlineFillUiInfo inlineFillUiInfo = - new InlineFillUi.InlineFillUiInfo(inlineSuggestionsRequest.get(), focusedId, - filterText, remoteRenderService, userId, id); - InlineFillUi inlineFillUi = InlineFillUi.forAutofill(inlineFillUiInfo, response, - new InlineFillUi.InlineSuggestionUiCallback() { - @Override - public void autofill(@NonNull Dataset dataset, int datasetIndex) { - fill(response.getRequestId(), datasetIndex, dataset, UI_TYPE_INLINE); - } + new InlineFillUi.InlineFillUiInfo( + inlineSuggestionsRequest.get(), + focusedId, + filterText, + remoteRenderService, + userId, + id); + InlineFillUi inlineFillUi = + InlineFillUi.forAutofill( + inlineFillUiInfo, + response, + new InlineFillUi.InlineSuggestionUiCallback() { + @Override + public void autofill(@NonNull Dataset dataset, int datasetIndex) { + fill( + response.getRequestId(), + datasetIndex, + dataset, + UI_TYPE_INLINE); + } - @Override - public void authenticate(int requestId, int datasetIndex) { - Session.this.authenticate(response.getRequestId(), datasetIndex, - response.getAuthentication(), response.getClientState(), - UI_TYPE_INLINE); - } + @Override + public void authenticate(int requestId, int datasetIndex) { + Session.this.authenticate( + response.getRequestId(), + datasetIndex, + response.getAuthentication(), + response.getClientState(), + UI_TYPE_INLINE); + } - @Override - public void startIntentSender(@NonNull IntentSender intentSender) { - Session.this.startIntentSender(intentSender, new Intent()); - } + @Override + public void startIntentSender(@NonNull IntentSender intentSender) { + Session.this.startIntentSender(intentSender, new Intent()); + } - @Override - public void onError() { - synchronized (mLock) { - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(focusedId)); - } - } + @Override + public void onError() { + synchronized (mLock) { + mInlineSessionController.setInlineFillUiLocked( + InlineFillUi.emptyUi(focusedId)); + } + } - @Override - public void onInflate() { - Session.this.onShown(UI_TYPE_INLINE, 1); - } - }, mService.getMaster().getMaxInputLengthForAutofill()); + @Override + public void onInflate() { + Session.this.onShown(UI_TYPE_INLINE, 1); + } + }, + mService.getMaster().getMaxInputLengthForAutofill()); return mInlineSessionController.setInlineFillUiLocked(inlineFillUi); } private ResultReceiver constructCredentialManagerCallback(int requestId) { - final ResultReceiver resultReceiver = new ResultReceiver(mHandler) { - final AutofillId mAutofillId = mCurrentViewId; - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == SUCCESS_CREDMAN_SELECTOR) { - Slog.d(TAG, "onReceiveResult from Credential Manager " - + "bottom sheet with mCurrentViewId: " + mAutofillId); - GetCredentialResponse getCredentialResponse = - resultData.getParcelable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, - GetCredentialResponse.class); - - if (Flags.autofillCredmanDevIntegration()) { - sendCredentialManagerResponseToApp(getCredentialResponse, - /*exception=*/ null, mAutofillId); - } else { - Dataset datasetFromCredential = getDatasetFromCredentialResponse( - getCredentialResponse); - if (datasetFromCredential != null) { - autoFill(requestId, /*datasetIndex=*/-1, - datasetFromCredential, false, - UI_TYPE_CREDMAN_BOTTOM_SHEET); + final ResultReceiver resultReceiver = + new ResultReceiver(mHandler) { + final AutofillId mAutofillId = mCurrentViewId; + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == SUCCESS_CREDMAN_SELECTOR) { + Slog.d( + TAG, + "onReceiveResult from Credential Manager " + + "bottom sheet with mCurrentViewId: " + + mAutofillId); + GetCredentialResponse getCredentialResponse = + resultData.getParcelable( + CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, + GetCredentialResponse.class); + + if (Flags.autofillCredmanDevIntegration()) { + sendCredentialManagerResponseToApp( + getCredentialResponse, /* exception= */ null, mAutofillId); + } else { + Dataset datasetFromCredential = + getDatasetFromCredentialResponse(getCredentialResponse); + if (datasetFromCredential != null) { + autoFill( + requestId, + /* datasetIndex= */ -1, + datasetFromCredential, + false, + UI_TYPE_CREDMAN_BOTTOM_SHEET); + } + } + } else if (resultCode == FAILURE_CREDMAN_SELECTOR) { + String[] exception = + resultData.getStringArray( + CredentialProviderService + .EXTRA_GET_CREDENTIAL_EXCEPTION); + if (exception != null && exception.length >= 2) { + String errType = exception[0]; + String errMsg = exception[1]; + Slog.w( + TAG, + "Credman bottom sheet from pinned " + + "entry failed with: + " + + errType + + " , " + + errMsg); + sendCredentialManagerResponseToApp( + /* response= */ null, + new GetCredentialException(errType, errMsg), + mAutofillId); + } + } else { + Slog.d( + TAG, + "Unknown resultCode from credential " + + "manager bottom sheet: " + + resultCode); } } - } else if (resultCode == FAILURE_CREDMAN_SELECTOR) { - String[] exception = resultData.getStringArray( - CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION); - if (exception != null && exception.length >= 2) { - String errType = exception[0]; - String errMsg = exception[1]; - Slog.w(TAG, "Credman bottom sheet from pinned " - + "entry failed with: + " + errType + " , " - + errMsg); - sendCredentialManagerResponseToApp(/*response=*/ null, - new GetCredentialException(errType, errMsg), - mAutofillId); - } - } else { - Slog.d(TAG, "Unknown resultCode from credential " - + "manager bottom sheet: " + resultCode); - } - } - }; - ResultReceiver ipcFriendlyResultReceiver = - toIpcFriendlyResultReceiver(resultReceiver); + }; + ResultReceiver ipcFriendlyResultReceiver = toIpcFriendlyResultReceiver(resultReceiver); return ipcFriendlyResultReceiver; } @@ -5309,8 +5874,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void notifyUnavailableToClient(int sessionFinishedState, - @Nullable ArrayList<AutofillId> autofillableIds) { + private void notifyUnavailableToClient( + int sessionFinishedState, @Nullable ArrayList<AutofillId> autofillableIds) { synchronized (mLock) { if (mCurrentViewId == null) return; try { @@ -5374,27 +5939,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (saveInfo.getRequiredIds() != null) { Collections.addAll(trackedViews, saveInfo.getRequiredIds()); mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE); + SAVE_UI_SHOWN_REASON_REQUIRED_ID_CHANGE); } if (saveInfo.getOptionalIds() != null) { Collections.addAll(trackedViews, saveInfo.getOptionalIds()); mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE); + SAVE_UI_SHOWN_REASON_OPTIONAL_ID_CHANGE); } } if ((flags & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) { - mSaveEventLogger.maybeSetSaveUiShownReason( - SAVE_UI_SHOWN_REASON_UNKNOWN); + mSaveEventLogger.maybeSetSaveUiShownReason(SAVE_UI_SHOWN_REASON_UNKNOWN); mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG); + NO_SAVE_REASON_WITH_DONT_SAVE_ON_FINISH_FLAG); saveOnFinish = false; } } else { flags = 0; - mSaveEventLogger.maybeSetSaveUiNotShownReason( - NO_SAVE_REASON_NO_SAVE_INFO); + mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NO_SAVE_INFO); saveTriggerId = null; } @@ -5427,21 +5990,35 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState try { if (sVerbose) { - Slog.v(TAG, "updateTrackedIdsLocked(): trackedViews: " + trackedViews - + " fillableIds: " + fillableIds + " triggerId: " + saveTriggerId - + " saveOnFinish:" + saveOnFinish + " flags: " + flags - + " hasSaveInfo: " + (saveInfo != null)); - } - mClient.setTrackedViews(id, toArray(trackedViews), mSaveOnAllViewsInvisible, - saveOnFinish, toArray(fillableIds), saveTriggerId, hasAuthentication); + Slog.v( + TAG, + "updateTrackedIdsLocked(): trackedViews: " + + trackedViews + + " fillableIds: " + + fillableIds + + " triggerId: " + + saveTriggerId + + " saveOnFinish:" + + saveOnFinish + + " flags: " + + flags + + " hasSaveInfo: " + + (saveInfo != null)); + } + mClient.setTrackedViews( + id, + toArray(trackedViews), + mSaveOnAllViewsInvisible, + saveOnFinish, + toArray(fillableIds), + saveTriggerId, + hasAuthentication); } catch (RemoteException e) { Slog.w(TAG, "Cannot set tracked ids", e); } } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ @GuardedBy("mLock") void setAutofillFailureLocked(@NonNull List<AutofillId> ids, boolean isRefill) { if (sVerbose && !ids.isEmpty()) { @@ -5464,9 +6041,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetViewFillFailureCounts(ids, isRefill); } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ @GuardedBy("mLock") void setViewAutofilledLocked(@NonNull AutofillId id) { if (sVerbose) { @@ -5478,17 +6053,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeAddSuccessId(id); } - /** - * Sets the state of views that failed to autofill. - */ + /** Sets the state of views that failed to autofill. */ void setNotifyNotExpiringResponseDuringAuth() { synchronized (mLock) { mPresentationStatsEventLogger.maybeSetNotifyNotExpiringResponseDuringAuth(); } } - /** - * Sets the state of views that failed to autofill. - */ + + /** Sets the state of views that failed to autofill. */ void setLogViewEnteredIgnoredDuringAuth() { synchronized (mLock) { mPresentationStatsEventLogger.notifyViewEnteredIgnoredDuringAuthCount(); @@ -5496,10 +6068,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void replaceResponseLocked(@NonNull FillResponse oldResponse, - @NonNull FillResponse newResponse, @Nullable Bundle newClientState) { + private void replaceResponseLocked( + @NonNull FillResponse oldResponse, + @NonNull FillResponse newResponse, + @Nullable Bundle newClientState) { // Disassociate view states with the old response - setViewStatesLocked(oldResponse, ViewState.STATE_INITIAL, /* clearResponse= */ true, + setViewStatesLocked( + oldResponse, + ViewState.STATE_INITIAL, + /* clearResponse= */ true, /* isPrimary= */ true); // Move over the id newResponse.setRequestId(oldResponse.getRequestId()); @@ -5519,7 +6096,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<AutofillId> autofillableIds; if (context != null) { final AssistStructure structure = context.getStructure(); - autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */true); + autofillableIds = Helper.getAutofillIds(structure, /* autofillableOnly= */ true); } else { Slog.w(TAG, "processNullResponseLocked(): no context for req " + requestId); autofillableIds = null; @@ -5535,8 +6112,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags); if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) { if (sVerbose) { - Slog.v(TAG, "canceling session " + id + " when service returned null and it cannot " - + "be augmented. AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "canceling session " + + id + + " when service returned null and it cannot " + + "be augmented. AutofillableIds: " + + autofillableIds); } // Nothing to be done, but need to notify client. notifyUnavailableToClient(AutofillManager.STATE_FINISHED, autofillableIds); @@ -5544,15 +6126,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } else { if ((flags & FLAG_PASSWORD_INPUT_TYPE) != 0) { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null and " - + "augmented service is disabled for password fields. " - + "AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "keeping session " + + id + + " when service returned null and " + + "augmented service is disabled for password fields. " + + "AutofillableIds: " + + autofillableIds); } mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); } else { if (sVerbose) { - Slog.v(TAG, "keeping session " + id + " when service returned null but " - + "it can be augmented. AutofillableIds: " + autofillableIds); + Slog.v( + TAG, + "keeping session " + + id + + " when service returned null but " + + "it can be augmented. AutofillableIds: " + + autofillableIds); } } mAugmentedAutofillableIds = autofillableIds; @@ -5567,8 +6159,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Tries to trigger Augmented Autofill when the standard service could not fulfill a request. * - * <p> The request may not have been sent when this method returns as it may be waiting for - * the inline suggestion request asynchronously. + * <p>The request may not have been sent when this method returns as it may be waiting for the + * inline suggestion request asynchronously. * * @return callback to destroy the autofill UI, or {@code null} if not supported. */ @@ -5582,8 +6174,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Check if Smart Suggestions is supported... - final @SmartSuggestionMode int supportedModes = mService - .getSupportedSmartSuggestionModesLocked(); + final @SmartSuggestionMode int supportedModes = + mService.getSupportedSmartSuggestionModesLocked(); if (supportedModes == 0) { if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no supported modes"); return null; @@ -5591,8 +6183,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...then if the service is set for the user - final RemoteAugmentedAutofillService remoteService = mService - .getRemoteAugmentedAutofillServiceLocked(); + final RemoteAugmentedAutofillService remoteService = + mService.getRemoteAugmentedAutofillServiceLocked(); if (remoteService == null) { if (sVerbose) Slog.v(TAG, "triggerAugmentedAutofillLocked(): no service for user"); return null; @@ -5612,25 +6204,37 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - final boolean isAllowlisted = mService - .isWhitelistedForAugmentedAutofillLocked(mComponentName); + final boolean isAllowlisted = + mService.isWhitelistedForAugmentedAutofillLocked(mComponentName); if (!isAllowlisted) { if (sVerbose) { - Slog.v(TAG, "triggerAugmentedAutofillLocked(): " - + ComponentName.flattenToShortString(mComponentName) + " not whitelisted "); - } - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - mCurrentViewId, isAllowlisted, /* isInline= */ null); + Slog.v( + TAG, + "triggerAugmentedAutofillLocked(): " + + ComponentName.flattenToShortString(mComponentName) + + " not whitelisted "); + } + logAugmentedAutofillRequestLocked( + mode, + remoteService.getComponentName(), + mCurrentViewId, + isAllowlisted, + /* isInline= */ null); return null; } if (sVerbose) { - Slog.v(TAG, "calling Augmented Autofill Service (" - + ComponentName.flattenToShortString(remoteService.getComponentName()) - + ") on view " + mCurrentViewId + " using suggestion mode " - + getSmartSuggestionModeToString(mode) - + " when server returned null for session " + this.id); + Slog.v( + TAG, + "calling Augmented Autofill Service (" + + ComponentName.flattenToShortString(remoteService.getComponentName()) + + ") on view " + + mCurrentViewId + + " using suggestion mode " + + getSmartSuggestionModeToString(mode) + + " when server returned null for session " + + this.id); } // Log FillRequest for Augmented Autofill. mFillRequestEventLogger.startLogForNewRequest(); @@ -5648,8 +6252,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mAugmentedRequestsLogs == null) { mAugmentedRequestsLogs = new ArrayList<>(); } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_AUGMENTED_REQUEST, - remoteService.getComponentName().getPackageName()); + final LogMaker log = + newLogMaker( + MetricsEvent.AUTOFILL_AUGMENTED_REQUEST, + remoteService.getComponentName().getPackageName()); mAugmentedRequestsLogs.add(log); final AutofillId focusedId = mCurrentViewId; @@ -5715,8 +6321,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } synchronized (session.mLock) { session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked( - mFocusedId, /*requestConsumer=*/ mRequestAugmentedAutofill, - result); + mFocusedId, /* requestConsumer= */ mRequestAugmentedAutofill, result); } } } @@ -5741,19 +6346,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mIsAllowlisted = isAllowlisted; mMode = mode; mCurrentValue = currentValue; - } + @Override public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) { Session session = mSessionWeakRef.get(); - if (logIfSessionNull( - session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { + if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { return; } session.onAugmentedAutofillInlineSuggestionAccept( inlineSuggestionsRequest, mFocusedId, mIsAllowlisted, mMode, mCurrentValue); - } } @@ -5770,8 +6373,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public Boolean apply(InlineFillUi inlineFillUi) { Session session = mSessionWeakRef.get(); - if (logIfSessionNull( - session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { + if (logIfSessionNull(session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { return false; } @@ -5801,8 +6403,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * If the session is null or has been destroyed, log the error msg, and return true. - * This is a helper function intended to be called when de-referencing from a weak reference. + * If the session is null or has been destroyed, log the error msg, and return true. This is a + * helper function intended to be called when de-referencing from a weak reference. + * * @param session * @param logPrefix * @return true if the session is null, false otherwise. @@ -5830,15 +6433,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { final RemoteAugmentedAutofillService remoteService = mService.getRemoteAugmentedAutofillServiceLocked(); - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - focussedId, isAllowlisted, inlineSuggestionsRequest != null); - remoteService.onRequestAutofillLocked(id, mClient, - taskId, mComponentName, mActivityToken, - AutofillId.withoutSession(focussedId), currentValue, + logAugmentedAutofillRequestLocked( + mode, + remoteService.getComponentName(), + focussedId, + isAllowlisted, + inlineSuggestionsRequest != null); + remoteService.onRequestAutofillLocked( + id, + mClient, + taskId, + mComponentName, + mActivityToken, + AutofillId.withoutSession(focussedId), + currentValue, inlineSuggestionsRequest, new AugmentedAutofillInlineSuggestionsResponseCallback(this), new AugmentedAutofillErrorCallback(this), - mService.getRemoteInlineSuggestionRenderServiceLocked(), userId); + mService.getRemoteInlineSuggestionRenderServiceLocked(), + userId); } } @@ -5847,15 +6460,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState cancelAugmentedAutofillLocked(); // Also cancel augmented in IME - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(mCurrentViewId)); + mInlineSessionController.setInlineFillUiLocked(InlineFillUi.emptyUi(mCurrentViewId)); } } @GuardedBy("mLock") private void cancelAugmentedAutofillLocked() { - final RemoteAugmentedAutofillService remoteService = mService - .getRemoteAugmentedAutofillServiceLocked(); + final RemoteAugmentedAutofillService remoteService = + mService.getRemoteAugmentedAutofillServiceLocked(); if (remoteService == null) { Slog.w(TAG, "cancelAugmentedAutofillLocked(): no service for user"); return; @@ -5865,8 +6477,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void processResponseLocked(@NonNull FillResponse newResponse, - @Nullable Bundle newClientState, int flags) { + private void processResponseLocked( + @NonNull FillResponse newResponse, @Nullable Bundle newClientState, int flags) { // Make sure we are hiding the UI which will be shown // only if handling the current response requires it. mUi.hideAll(this); @@ -5878,9 +6490,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int requestId = newResponse.getRequestId(); if (sVerbose) { - Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId - + ",flags=" + flags + ", reqId=" + requestId + ", resp=" + newResponse - + ",newClientState=" + newClientState); + Slog.v( + TAG, + "processResponseLocked(): mCurrentViewId=" + + mCurrentViewId + + ",flags=" + + flags + + ", reqId=" + + requestId + + ", resp=" + + newResponse + + ",newClientState=" + + newClientState); } if (mResponses == null) { @@ -5892,8 +6513,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mResponses.put(requestId, newResponse); mClientState = newClientState != null ? newClientState : newResponse.getClientState(); - boolean webviewRequestedCredman = newClientState != null && newClientState.getBoolean( - WEBVIEW_REQUESTED_CREDENTIAL_KEY, false); + boolean webviewRequestedCredman = + newClientState != null + && newClientState.getBoolean(WEBVIEW_REQUESTED_CREDENTIAL_KEY, false); List<Dataset> datasetList = newResponse.getDatasets(); mPresentationStatsEventLogger.maybeSetWebviewRequestedCredential(webviewRequestedCredman); @@ -5901,7 +6523,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId); mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList); - setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, /* clearResponse= */ false, + setViewStatesLocked( + newResponse, + ViewState.STATE_FILLABLE, + /* clearResponse= */ false, /* isPrimary= */ true); updateFillDialogTriggerIdsLocked(); updateTrackedIdsLocked(); @@ -5914,12 +6539,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.maybeCallOnFillReady(flags); } - /** - * Sets the state of all views in the given response. - */ + /** Sets the state of all views in the given response. */ @GuardedBy("mLock") - private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse, - boolean isPrimary) { + private void setViewStatesLocked( + FillResponse response, int state, boolean clearResponse, boolean isPrimary) { final List<Dataset> datasets = response.getDatasets(); if (datasets != null && !datasets.isEmpty()) { for (int i = 0; i < datasets.size(); i++) { @@ -5964,12 +6587,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Sets the state and response of all views in the given dataset. - */ + /** Sets the state and response of all views in the given dataset. */ @GuardedBy("mLock") - private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset, - int state, boolean clearResponse, boolean isPrimary) { + private void setViewStatesLocked( + @Nullable FillResponse response, + @NonNull Dataset dataset, + int state, + boolean clearResponse, + boolean isPrimary) { final ArrayList<AutofillId> ids = dataset.getFieldIds(); final ArrayList<AutofillValue> values = dataset.getFieldValues(); for (int j = 0; j < ids.size(); j++) { @@ -5989,10 +6614,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state, - @Nullable AutofillValue value) { + private ViewState createOrUpdateViewStateLocked( + @NonNull AutofillId id, int state, @Nullable AutofillValue value) { ViewState viewState = mViewStates.get(id); - if (viewState != null) { + if (viewState != null) { viewState.setState(state); } else { viewState = new ViewState(id, this, state, mIsPrimaryCredential); @@ -6008,22 +6633,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return viewState; } - void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, - int uiType) { + void autoFill( + int requestId, int datasetIndex, Dataset dataset, boolean generateEvent, int uiType) { if (sDebug) { - Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex - + "; dataset=" + dataset); + Slog.d( + TAG, + "autoFill(): requestId=" + + requestId + + "; datasetIdx=" + + datasetIndex + + "; dataset=" + + dataset); } synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#autoFill() rejected - session: " - + id + " destroyed"); + Slog.w(TAG, "Call to Session#autoFill() rejected - session: " + id + " destroyed"); return; } // Selected dataset id is logged regardless of authentication result. mPresentationStatsEventLogger.maybeSetSelectedDatasetId(datasetIndex); mPresentationStatsEventLogger.maybeSetSelectedDatasetPickReason( - dataset.getEligibleReason()); + dataset.getEligibleReason()); // Autofill it directly... if (dataset.getAuthentication() == null) { if (generateEvent) { @@ -6039,10 +6669,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // ...or handle authentication. mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType); mPresentationStatsEventLogger.maybeSetAuthenticationType( - AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); + AUTHENTICATION_TYPE_DATASET_AUTHENTICATION); // does not matter the value of isPrimary because null response won't be overridden. - setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, - /* clearResponse= */ false, /* isPrimary= */ true); + setViewStatesLocked( + null, + dataset, + ViewState.STATE_WAITING_DATASET_AUTH, + /* clearResponse= */ false, + /* isPrimary= */ true); final Intent fillInIntent; if (dataset.getCredentialFillInIntent() != null && Flags.autofillCredmanIntegration()) { Slog.d(TAG, "Setting credential fill intent"); @@ -6055,11 +6689,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState forceRemoveFromServiceLocked(); return; } - final int authenticationId = AutofillManager.makeAuthenticationId(requestId, - datasetIndex); - startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent, - /* authenticateInline= */false); - + final int authenticationId = + AutofillManager.makeAuthenticationId(requestId, datasetIndex); + startAuthentication( + authenticationId, + dataset.getAuthentication(), + fillInIntent, + /* authenticateInline= */ false); } } @@ -6072,13 +6708,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final FillContext context = getFillContextByRequestIdLocked(requestId); if (context == null) { - wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s", - requestId, mContexts); + wtf( + null, + "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s", + requestId, + mContexts); return null; } if (mLastInlineSuggestionsRequest != null && mLastInlineSuggestionsRequest.first == requestId) { - fillInIntent.putExtra(AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST, + fillInIntent.putExtra( + AutofillManager.EXTRA_INLINE_SUGGESTIONS_REQUEST, mLastInlineSuggestionsRequest.second); } fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure()); @@ -6121,12 +6761,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void startAuthentication(int authenticationId, IntentSender intent, - Intent fillInIntent, boolean authenticateInline) { + private void startAuthentication( + int authenticationId, + IntentSender intent, + Intent fillInIntent, + boolean authenticateInline) { try { synchronized (mLock) { - mClient.authenticate(id, authenticationId, intent, fillInIntent, - authenticateInline); + mClient.authenticate( + id, authenticationId, intent, fillInIntent, authenticateInline); } } catch (RemoteException e) { Slog.e(TAG, "Error launching auth intent", e); @@ -6139,22 +6782,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * @hide */ static final class SaveResult { - /** - * Whether to record the save dialog has been shown. - */ + /** Whether to record the save dialog has been shown. */ private boolean mLogSaveShown; - /** - * Whether to remove the session. - */ + /** Whether to remove the session. */ private boolean mRemoveSession; - /** - * The reason why a save dialog was not shown. - */ + /** The reason why a save dialog was not shown. */ @NoSaveReason private int mSaveDialogNotShowReason; - SaveResult(boolean logSaveShown, boolean removeSession, + SaveResult( + boolean logSaveShown, + boolean removeSession, @NoSaveReason int saveDialogNotShowReason) { mLogSaveShown = logSaveShown; mRemoveSession = removeSession; @@ -6218,15 +6857,19 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { - return "SaveResult: [logSaveShown=" + mLogSaveShown - + ", removeSession=" + mRemoveSession - + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; + return "SaveResult: [logSaveShown=" + + mLogSaveShown + + ", removeSession=" + + mRemoveSession + + ", saveDialogNotShowReason=" + + mSaveDialogNotShowReason + + "]"; } } /** - * Class maintaining the state of the requests to - * {@link android.service.assist.classification.FieldClassificationService}. + * Class maintaining the state of the requests to {@link + * android.service.assist.classification.FieldClassificationService}. */ private static final class ClassificationState { @@ -6234,18 +6877,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Initial state indicating that the request for classification hasn't been triggered yet. */ private static final int STATE_INITIAL = 1; - /** - * Assist request has been triggered, but awaiting response. - */ + + /** Assist request has been triggered, but awaiting response. */ private static final int STATE_PENDING_ASSIST_REQUEST = 2; - /** - * Classification request has been triggered, but awaiting response. - */ + + /** Classification request has been triggered, but awaiting response. */ private static final int STATE_PENDING_REQUEST = 3; - /** - * Classification response has been received. - */ + + /** Classification response has been received. */ private static final int STATE_RESPONSE = 4; + /** * Classification state has been invalidated, and the last response may no longer be valid. * This could occur due to various reasons like views changing their layouts, becoming @@ -6254,15 +6895,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private static final int STATE_INVALIDATED = 5; - @IntDef(prefix = { "STATE_" }, value = { - STATE_INITIAL, - STATE_PENDING_ASSIST_REQUEST, - STATE_PENDING_REQUEST, - STATE_RESPONSE, - STATE_INVALIDATED - }) + @IntDef( + prefix = {"STATE_"}, + value = { + STATE_INITIAL, + STATE_PENDING_ASSIST_REQUEST, + STATE_PENDING_REQUEST, + STATE_RESPONSE, + STATE_INVALIDATED + }) @Retention(RetentionPolicy.SOURCE) - @interface ClassificationRequestState{} + @interface ClassificationRequestState {} @GuardedBy("mLock") private @ClassificationRequestState int mState = STATE_INITIAL; @@ -6284,8 +6927,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Typically, there would be a 1:1 mapping. However, in certain cases, we may have a hint - * being applicable to many types. An example of this being new/change password forms, - * where you need to confirm the passward twice. + * being applicable to many types. An example of this being new/change password forms, where + * you need to confirm the passward twice. */ @GuardedBy("mLock") private ArrayMap<String, Set<AutofillId>> mHintsToAutofillIdMap; @@ -6317,8 +6960,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState /** * Process the response received. + * * @return true if the response was processed, false otherwise. If there wasn't any - * response, yet this function was called, it would return false. + * response, yet this function was called, it would return false. */ @GuardedBy("mLock") private boolean processResponse() { @@ -6358,7 +7002,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private static void processDetections(Set<String> detections, AutofillId id, + private static void processDetections( + Set<String> detections, + AutofillId id, ArrayMap<String, Set<AutofillId>> currentMap) { for (String detection : detections) { Set<AutofillId> autofillIds; @@ -6416,37 +7062,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public String toString() { return "ClassificationState: [" - + "state=" + stateToString() - + ", mPendingFieldClassificationRequest=" + mPendingFieldClassificationRequest - + ", mLastFieldClassificationResponse=" + mLastFieldClassificationResponse - + ", mClassificationHintsMap=" + mClassificationHintsMap - + ", mClassificationGroupHintsMap=" + mClassificationGroupHintsMap - + ", mHintsToAutofillIdMap=" + mHintsToAutofillIdMap - + ", mGroupHintsToAutofillIdMap=" + mGroupHintsToAutofillIdMap + + "state=" + + stateToString() + + ", mPendingFieldClassificationRequest=" + + mPendingFieldClassificationRequest + + ", mLastFieldClassificationResponse=" + + mLastFieldClassificationResponse + + ", mClassificationHintsMap=" + + mClassificationHintsMap + + ", mClassificationGroupHintsMap=" + + mClassificationGroupHintsMap + + ", mHintsToAutofillIdMap=" + + mHintsToAutofillIdMap + + ", mGroupHintsToAutofillIdMap=" + + mGroupHintsToAutofillIdMap + "]"; } - } @Override public String toString() { - return "Session: [id=" + id + ", component=" + mComponentName - + ", state=" + sessionStateAsString(mSessionState) + "]"; + return "Session: [id=" + + id + + ", component=" + + mComponentName + + ", state=" + + sessionStateAsString(mSessionState) + + "]"; } @GuardedBy("mLock") void dumpLocked(String prefix, PrintWriter pw) { final String prefix2 = prefix + " "; - pw.print(prefix); pw.print("id: "); pw.println(id); - pw.print(prefix); pw.print("uid: "); pw.println(uid); - pw.print(prefix); pw.print("taskId: "); pw.println(taskId); - pw.print(prefix); pw.print("flags: "); pw.println(mFlags); - pw.print(prefix); pw.print("displayId: "); pw.println(mContext.getDisplayId()); - pw.print(prefix); pw.print("state: "); pw.println(sessionStateAsString(mSessionState)); - pw.print(prefix); pw.print("mComponentName: "); pw.println(mComponentName); - pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); - pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); - pw.print(prefix); pw.print("Time to show UI: "); + pw.print(prefix); + pw.print("id: "); + pw.println(id); + pw.print(prefix); + pw.print("uid: "); + pw.println(uid); + pw.print(prefix); + pw.print("taskId: "); + pw.println(taskId); + pw.print(prefix); + pw.print("flags: "); + pw.println(mFlags); + pw.print(prefix); + pw.print("displayId: "); + pw.println(mContext.getDisplayId()); + pw.print(prefix); + pw.print("state: "); + pw.println(sessionStateAsString(mSessionState)); + pw.print(prefix); + pw.print("mComponentName: "); + pw.println(mComponentName); + pw.print(prefix); + pw.print("mActivityToken: "); + pw.println(mActivityToken); + pw.print(prefix); + pw.print("mStartTime: "); + pw.println(mStartTime); + pw.print(prefix); + pw.print("Time to show UI: "); if (mUiShownTime == 0) { pw.println("N/A"); } else { @@ -6454,41 +7130,67 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.println(); } final int requestLogsSizes = mRequestLogs.size(); - pw.print(prefix); pw.print("mSessionLogs: "); pw.println(requestLogsSizes); + pw.print(prefix); + pw.print("mSessionLogs: "); + pw.println(requestLogsSizes); for (int i = 0; i < requestLogsSizes; i++) { final int requestId = mRequestLogs.keyAt(i); final LogMaker log = mRequestLogs.valueAt(i); - pw.print(prefix2); pw.print('#'); pw.print(i); pw.print(": req="); - pw.print(requestId); pw.print(", log=" ); dumpRequestLog(pw, log); pw.println(); + pw.print(prefix2); + pw.print('#'); + pw.print(i); + pw.print(": req="); + pw.print(requestId); + pw.print(", log="); + dumpRequestLog(pw, log); + pw.println(); } - pw.print(prefix); pw.print("mResponses: "); + pw.print(prefix); + pw.print("mResponses: "); if (mResponses == null) { pw.println("null"); } else { pw.println(mResponses.size()); for (int i = 0; i < mResponses.size(); i++) { - pw.print(prefix2); pw.print('#'); pw.print(i); - pw.print(' '); pw.println(mResponses.valueAt(i)); - } - } - pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId); - pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed); - pw.print(prefix); pw.print("mShowingSaveUi: "); pw.println(mSessionFlags.mShowingSaveUi); - pw.print(prefix); pw.print("mPendingSaveUi: "); pw.println(mPendingSaveUi); + pw.print(prefix2); + pw.print('#'); + pw.print(i); + pw.print(' '); + pw.println(mResponses.valueAt(i)); + } + } + pw.print(prefix); + pw.print("mCurrentViewId: "); + pw.println(mCurrentViewId); + pw.print(prefix); + pw.print("mDestroyed: "); + pw.println(mDestroyed); + pw.print(prefix); + pw.print("mShowingSaveUi: "); + pw.println(mSessionFlags.mShowingSaveUi); + pw.print(prefix); + pw.print("mPendingSaveUi: "); + pw.println(mPendingSaveUi); final int numberViews = mViewStates.size(); - pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size()); + pw.print(prefix); + pw.print("mViewStates size: "); + pw.println(mViewStates.size()); for (int i = 0; i < numberViews; i++) { - pw.print(prefix); pw.print("ViewState at #"); pw.println(i); + pw.print(prefix); + pw.print("ViewState at #"); + pw.println(i); mViewStates.valueAt(i).dump(prefix2, pw); } - pw.print(prefix); pw.print("mContexts: " ); + pw.print(prefix); + pw.print("mContexts: "); if (mContexts != null) { int numContexts = mContexts.size(); for (int i = 0; i < numContexts; i++) { FillContext context = mContexts.get(i); - pw.print(prefix2); pw.print(context); + pw.print(prefix2); + pw.print(context); if (sVerbose) { pw.println("AssistStructure dumped at logcat)"); @@ -6500,43 +7202,62 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.println("null"); } - pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback); + pw.print(prefix); + pw.print("mHasCallback: "); + pw.println(mHasCallback); if (mClientState != null) { - pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw - .println(" bytes"); - } - pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode); - pw.print(prefix); pw.print("mUrlBar: "); + pw.print(prefix); + pw.print("mClientState: "); + pw.print(mClientState.getSize()); + pw.println(" bytes"); + } + pw.print(prefix); + pw.print("mCompatMode: "); + pw.println(mCompatMode); + pw.print(prefix); + pw.print("mUrlBar: "); if (mUrlBar == null) { pw.println("N/A"); } else { - pw.print("id="); pw.print(mUrlBar.getAutofillId()); - pw.print(" domain="); pw.print(mUrlBar.getWebDomain()); - pw.print(" text="); Helper.printlnRedactedText(pw, mUrlBar.getText()); - } - pw.print(prefix); pw.print("mSaveOnAllViewsInvisible: "); pw.println( - mSaveOnAllViewsInvisible); - pw.print(prefix); pw.print("mSelectedDatasetIds: "); pw.println(mSelectedDatasetIds); + pw.print("id="); + pw.print(mUrlBar.getAutofillId()); + pw.print(" domain="); + pw.print(mUrlBar.getWebDomain()); + pw.print(" text="); + Helper.printlnRedactedText(pw, mUrlBar.getText()); + } + pw.print(prefix); + pw.print("mSaveOnAllViewsInvisible: "); + pw.println(mSaveOnAllViewsInvisible); + pw.print(prefix); + pw.print("mSelectedDatasetIds: "); + pw.println(mSelectedDatasetIds); if (mSessionFlags.mAugmentedAutofillOnly) { - pw.print(prefix); pw.println("For Augmented Autofill Only"); + pw.print(prefix); + pw.println("For Augmented Autofill Only"); } if (mSessionFlags.mFillDialogDisabled) { - pw.print(prefix); pw.println("Fill Dialog disabled"); + pw.print(prefix); + pw.println("Fill Dialog disabled"); } if (mLastFillDialogTriggerIds != null) { - pw.print(prefix); pw.println("Last Fill Dialog trigger ids: "); + pw.print(prefix); + pw.println("Last Fill Dialog trigger ids: "); pw.println(mSelectedDatasetIds); } if (mAugmentedAutofillDestroyer != null) { - pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer"); + pw.print(prefix); + pw.println("has mAugmentedAutofillDestroyer"); } if (mAugmentedRequestsLogs != null) { - pw.print(prefix); pw.print("number augmented requests: "); + pw.print(prefix); + pw.print("number augmented requests: "); pw.println(mAugmentedRequestsLogs.size()); } if (mAugmentedAutofillableIds != null) { - pw.print(prefix); pw.print("mAugmentedAutofillableIds: "); + pw.print(prefix); + pw.print("mAugmentedAutofillableIds: "); pw.println(mAugmentedAutofillableIds); } if (mRemoteFillService != null) { @@ -6545,21 +7266,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } private static void dumpRequestLog(@NonNull PrintWriter pw, @NonNull LogMaker log) { - pw.print("CAT="); pw.print(log.getCategory()); + pw.print("CAT="); + pw.print(log.getCategory()); pw.print(", TYPE="); final int type = log.getType(); switch (type) { - case MetricsEvent.TYPE_SUCCESS: pw.print("SUCCESS"); break; - case MetricsEvent.TYPE_FAILURE: pw.print("FAILURE"); break; - case MetricsEvent.TYPE_CLOSE: pw.print("CLOSE"); break; - default: pw.print("UNSUPPORTED"); - } - pw.print('('); pw.print(type); pw.print(')'); - pw.print(", PKG="); pw.print(log.getPackageName()); - pw.print(", SERVICE="); pw.print(log - .getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE)); - pw.print(", ORDINAL="); pw.print(log - .getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL)); + case MetricsEvent.TYPE_SUCCESS: + pw.print("SUCCESS"); + break; + case MetricsEvent.TYPE_FAILURE: + pw.print("FAILURE"); + break; + case MetricsEvent.TYPE_CLOSE: + pw.print("CLOSE"); + break; + default: + pw.print("UNSUPPORTED"); + } + pw.print('('); + pw.print(type); + pw.print(')'); + pw.print(", PKG="); + pw.print(log.getPackageName()); + pw.print(", SERVICE="); + pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE)); + pw.print(", ORDINAL="); + pw.print(log.getTaggedData(MetricsEvent.FIELD_AUTOFILL_REQUEST_ORDINAL)); dumpNumericValue(pw, log, "FLAGS", MetricsEvent.FIELD_AUTOFILL_FLAGS); dumpNumericValue(pw, log, "NUM_DATASETS", MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS); dumpNumericValue(pw, log, "UI_LATENCY", MetricsEvent.FIELD_AUTOFILL_DURATION); @@ -6569,64 +7301,86 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(", AUTH_STATUS="); switch (authStatus) { case MetricsEvent.AUTOFILL_AUTHENTICATED: - pw.print("AUTHENTICATED"); break; + pw.print("AUTHENTICATED"); + break; case MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED: - pw.print("DATASET_AUTHENTICATED"); break; + pw.print("DATASET_AUTHENTICATED"); + break; case MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION: - pw.print("INVALID_AUTHENTICATION"); break; + pw.print("INVALID_AUTHENTICATION"); + break; case MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION: - pw.print("INVALID_DATASET_AUTHENTICATION"); break; - default: pw.print("UNSUPPORTED"); + pw.print("INVALID_DATASET_AUTHENTICATION"); + break; + default: + pw.print("UNSUPPORTED"); } - pw.print('('); pw.print(authStatus); pw.print(')'); + pw.print('('); + pw.print(authStatus); + pw.print(')'); } - dumpNumericValue(pw, log, "FC_IDS", - MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS); - dumpNumericValue(pw, log, "COMPAT_MODE", - MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE); + dumpNumericValue( + pw, log, "FC_IDS", MetricsEvent.FIELD_AUTOFILL_NUM_FIELD_CLASSIFICATION_IDS); + dumpNumericValue(pw, log, "COMPAT_MODE", MetricsEvent.FIELD_AUTOFILL_COMPAT_MODE); } - private static void dumpNumericValue(@NonNull PrintWriter pw, @NonNull LogMaker log, - @NonNull String field, int tag) { + private static void dumpNumericValue( + @NonNull PrintWriter pw, @NonNull LogMaker log, @NonNull String field, int tag) { final int value = getNumericValue(log, tag); if (value != 0) { - pw.print(", "); pw.print(field); pw.print('='); pw.print(value); + pw.print(", "); + pw.print(field); + pw.print('='); + pw.print(value); } } - void sendCredentialManagerResponseToApp(@Nullable GetCredentialResponse response, - @Nullable GetCredentialException exception, @NonNull AutofillId viewId) { + void sendCredentialManagerResponseToApp( + @Nullable GetCredentialResponse response, + @Nullable GetCredentialException exception, + @NonNull AutofillId viewId) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#sendCredentialManagerResponseToApp() rejected " - + "- session: " + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#sendCredentialManagerResponseToApp() rejected " + + "- session: " + + id + + " destroyed"); return; } try { final ViewState viewState = mViewStates.get(viewId); if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() - && viewState != null && viewState.id.getSessionId() != id) { + && viewState != null + && viewState.id.getSessionId() != id) { if (sVerbose) { - Slog.v(TAG, "Skipping sending credential response to view: " - + viewId + " as it isn't part of the current session: " + id); + Slog.v( + TAG, + "Skipping sending credential response to view: " + + viewId + + " as it isn't part of the current session: " + + id); } } if (exception != null) { if (viewId.isVirtualInt()) { - sendResponseToViewNode(viewId, /*response=*/ null, exception); + sendResponseToViewNode(viewId, /* response= */ null, exception); } else { - mClient.onGetCredentialException(id, viewId, exception.getType(), - exception.getMessage()); + mClient.onGetCredentialException( + id, viewId, exception.getType(), exception.getMessage()); } } else if (response != null) { if (viewId.isVirtualInt()) { - sendResponseToViewNode(viewId, response, /*exception=*/ null); + sendResponseToViewNode(viewId, response, /* exception= */ null); } else { mClient.onGetCredentialResponse(id, viewId, response); } } else { - Slog.w(TAG, "sendCredentialManagerResponseToApp called with null response" - + "and exception"); + Slog.w( + TAG, + "sendCredentialManagerResponseToApp called with null response" + + "and exception"); } } catch (RemoteException e) { Slog.w(TAG, "Error sending credential response to activity: " + e); @@ -6635,23 +7389,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void sendResponseToViewNode(AutofillId viewId, GetCredentialResponse response, - GetCredentialException exception) { + private void sendResponseToViewNode( + AutofillId viewId, GetCredentialResponse response, GetCredentialException exception) { ViewNode viewNode = getViewNodeFromContextsLocked(viewId); if (viewNode != null && viewNode.getPendingCredentialCallback() != null) { Bundle resultData = new Bundle(); if (response != null) { resultData.putParcelable( - CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, - response); - viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, - resultData); + CredentialProviderService.EXTRA_GET_CREDENTIAL_RESPONSE, response); + viewNode.getPendingCredentialCallback().send(SUCCESS_CREDMAN_SELECTOR, resultData); } else if (exception != null) { resultData.putStringArray( CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION, new String[] {exception.getType(), exception.getMessage()}); - viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, - resultData); + viewNode.getPendingCredentialCallback().send(FAILURE_CREDMAN_SELECTOR, resultData); } } else { Slog.w(TAG, "View node not found after GetCredentialResponse"); @@ -6661,8 +7412,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void autoFillApp(Dataset dataset) { synchronized (mLock) { if (mDestroyed) { - Slog.w(TAG, "Call to Session#autoFillApp() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#autoFillApp() rejected - session: " + id + " destroyed"); return; } try { @@ -6671,8 +7423,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final List<AutofillId> ids = new ArrayList<>(entryCount); final List<AutofillValue> values = new ArrayList<>(entryCount); boolean waitingDatasetAuth = false; - boolean hideHighlight = (entryCount == 1 - && dataset.getFieldIds().get(0).equals(mCurrentViewId)); + boolean hideHighlight = + highlightAutofillSingleField() + ? false + : (entryCount == 1 + && dataset.getFieldIds().get(0).equals(mCurrentViewId)); // Count how many views are filtered because they are not in current session int numOfViewsFiltered = 0; for (int i = 0; i < entryCount; i++) { @@ -6682,10 +7437,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId viewId = dataset.getFieldIds().get(i); final ViewState viewState = mViewStates.get(viewId); if (mService.getMaster().getIsFillFieldsFromCurrentSessionOnly() - && viewState != null && viewState.id.getSessionId() != id) { + && viewState != null + && viewState.id.getSessionId() != id) { if (sVerbose) { - Slog.v(TAG, "Skipping filling view: " + - viewId + " as it isn't part of the current session: " + id); + Slog.v( + TAG, + "Skipping filling view: " + + viewId + + " as it isn't part of the current session: " + + id); } numOfViewsFiltered += 1; continue; @@ -6721,8 +7481,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // does not matter the value of isPrimary because null response won't be // overridden. - setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, - /* clearResponse= */ false, /* isPrimary= */ true); + setViewStatesLocked( + null, + dataset, + ViewState.STATE_AUTOFILLED, + /* clearResponse= */ false, + /* isPrimary= */ true); } } catch (RemoteException e) { Slog.w(TAG, "Error autofilling activity: " + e); @@ -6750,7 +7514,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionCommittedEventLogger.maybeSetCommitReason(val); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); mSessionCommittedEventLogger.maybeSetSessionDurationMillis( - SystemClock.elapsedRealtime() - mStartTime); + SystemClock.elapsedRealtime() - mStartTime); mFillRequestEventLogger.logAndEndEvent(); mFillResponseEventLogger.logAndEndEvent(); mPresentationStatsEventLogger.logAndEndEvent("log all events"); @@ -6808,8 +7572,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final int totalAugmentedRequests = mAugmentedRequestsLogs == null ? 0 - : mAugmentedRequestsLogs.size(); + final int totalAugmentedRequests = + mAugmentedRequestsLogs == null ? 0 : mAugmentedRequestsLogs.size(); if (totalAugmentedRequests > 0) { if (sVerbose) { Slog.v(TAG, "destroyLocked(): logging " + totalRequests + " augmented requests"); @@ -6820,11 +7584,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests); + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_SESSION_FINISHED) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_REQUESTS, totalRequests); if (totalAugmentedRequests > 0) { - log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, - totalAugmentedRequests); + log.addTaggedData( + MetricsEvent.FIELD_AUTOFILL_NUMBER_AUGMENTED_REQUESTS, totalAugmentedRequests); } if (mSessionFlags.mAugmentedAutofillOnly) { log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_AUGMENTED_ONLY, 1); @@ -6846,8 +7611,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") void forceRemoveFromServiceIfForAugmentedOnlyLocked() { if (sVerbose) { - Slog.v(TAG, "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + this.id + "): " - + mSessionFlags.mAugmentedAutofillOnly); + Slog.v( + TAG, + "forceRemoveFromServiceIfForAugmentedOnlyLocked(" + + this.id + + "): " + + mSessionFlags.mAugmentedAutofillOnly); } if (!mSessionFlags.mAugmentedAutofillOnly) return; @@ -6880,9 +7649,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Thread-safe version of {@link #removeFromServiceLocked()}. - */ + /** Thread-safe version of {@link #removeFromServiceLocked()}. */ private void removeFromService() { synchronized (mLock) { removeFromServiceLocked(); @@ -6897,8 +7664,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void removeFromServiceLocked() { if (sVerbose) Slog.v(TAG, "removeFromServiceLocked(" + this.id + "): " + mPendingSaveUi); if (mDestroyed) { - Slog.w(TAG, "Call to Session#removeFromServiceLocked() rejected - session: " - + id + " destroyed"); + Slog.w( + TAG, + "Call to Session#removeFromServiceLocked() rejected - session: " + + id + + " destroyed"); return; } if (isSaveUiPendingLocked()) { @@ -6922,18 +7692,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** - * Checks whether this session is hiding the Save UI to handle a custom description link for - * a specific {@code token} created by - * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}. + * Checks whether this session is hiding the Save UI to handle a custom description link for a + * specific {@code token} created by {@link PendingUi#PendingUi(IBinder, int, + * IAutoFillManagerClient)}. */ @GuardedBy("mLock") boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) { return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken()); } - /** - * Checks whether this session is hiding the Save UI to handle a custom description link. - */ + /** Checks whether this session is hiding the Save UI to handle a custom description link. */ @GuardedBy("mLock") private boolean isSaveUiPendingLocked() { return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING; @@ -6942,8 +7710,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Return latest response index in mResponses SparseArray. @GuardedBy("mLock") private int getLastResponseIndexLocked() { - if (mResponses == null || mResponses.size() == 0) { - return -1; + if (mResponses == null || mResponses.size() == 0) { + return -1; } List<Integer> requestIdList = new ArrayList<>(); final int responseCount = mResponses.size(); @@ -6967,15 +7735,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void logAuthenticationStatusLocked(int requestId, int status) { - addTaggedDataToRequestLogLocked(requestId, - MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status); + addTaggedDataToRequestLogLocked( + requestId, MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status); } @GuardedBy("mLock") private void addTaggedDataToRequestLogLocked(int requestId, int tag, @Nullable Object value) { final LogMaker requestLog = mRequestLogs.get(requestId); if (requestLog == null) { - Slog.w(TAG, + Slog.w( + TAG, "addTaggedDataToRequestLogLocked(tag=" + tag + "): no log for id " + requestId); return; } @@ -6983,20 +7752,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logAugmentedAutofillRequestLocked(int mode, - ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted, + private void logAugmentedAutofillRequestLocked( + int mode, + ComponentName augmentedRemoteServiceName, + AutofillId focusedId, + boolean isWhitelisted, Boolean isInline) { final String historyItem = - "aug:id=" + id + " u=" + uid + " m=" + mode - + " a=" + ComponentName.flattenToShortString(mComponentName) - + " f=" + focusedId - + " s=" + augmentedRemoteServiceName - + " w=" + isWhitelisted - + " i=" + isInline; + "aug:id=" + + id + + " u=" + + uid + + " m=" + + mode + + " a=" + + ComponentName.flattenToShortString(mComponentName) + + " f=" + + focusedId + + " s=" + + augmentedRemoteServiceName + + " w=" + + isWhitelisted + + " i=" + + isInline; mService.getMaster().logRequestLocked(historyItem); } - private void wtf(@Nullable Exception e, String fmt, Object...args) { + private void wtf(@Nullable Exception e, String fmt, Object... args) { final String message = String.format(fmt, args); synchronized (mLock) { mWtfHistory.log(message); @@ -7051,13 +7833,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mClassificationState.updateResponseReceived(response); } - public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) { + public void onClassificationRequestFailure(int requestId, @Nullable CharSequence message) {} - } - - public void onClassificationRequestTimeout(int requestId) { - - } + public void onClassificationRequestTimeout(int requestId) {} @Override public void onServiceDied(@NonNull RemoteFieldClassificationService service) { @@ -7070,12 +7848,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Override public void logFieldClassificationEvent( - long startTime, FieldClassificationResponse response, + long startTime, + FieldClassificationResponse response, @FieldClassificationEventLogger.FieldClassificationStatus int status) { final FieldClassificationEventLogger logger = FieldClassificationEventLogger.createLogger(); logger.startNewLogForRequest(); - logger.maybeSetLatencyMillis( - SystemClock.elapsedRealtime() - startTime); + logger.maybeSetLatencyMillis(SystemClock.elapsedRealtime() - startTime); logger.maybeSetAppPackageUid(uid); logger.maybeSetNextFillRequestId(mFillRequestIdSnapshot + 1); logger.maybeSetRequestId(sIdCounterForPcc.get()); diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java index 6f0baef019b3..150e8da5f614 100644 --- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java +++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java @@ -16,7 +16,7 @@ package com.android.server.companion.association; -import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; import static com.android.internal.util.CollectionUtils.any; @@ -107,7 +107,7 @@ public class DisassociationProcessor { it -> deviceProfile.equals(it.getDeviceProfile()) && id != it.getId()); final int packageProcessImportance = getPackageProcessImportance(userId, packageName); - if (packageProcessImportance <= IMPORTANCE_VISIBLE && deviceProfile != null + if (packageProcessImportance <= IMPORTANCE_FOREGROUND && deviceProfile != null && !isRoleInUseByOtherAssociations) { // Need to remove the app from the list of role holders, but the process is visible // to the user at the moment, so we'll need to do it later. @@ -238,12 +238,16 @@ public class DisassociationProcessor { */ private class OnPackageVisibilityChangeListener implements ActivityManager.OnUidImportanceListener { - + // This method is called when the importance of a uid (app) changes. + // We only care about changes where the app is moving to the background. + // (e.g., the app currently is not at the top of the screen that the user + // is interacting with.) @Override public void onUidImportance(int uid, int importance) { - if (importance <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { - // The lower the importance value the more "important" the process is. - // We are only interested when the process ceases to be visible. + // Higher importance values indicate the app is less important. + // We are only interested when the process importance level + // is greater than IMPORTANCE_FOREGROUND. + if (importance <= IMPORTANCE_FOREGROUND) { return; } diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java index 57c643bb08a1..a7aab49cde56 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java @@ -160,11 +160,13 @@ public class ContentSuggestionsManagerService extends HardwareBuffer snapshotBuffer = null; int colorSpaceId = 0; + TaskSnapshot snapshot = null; // Skip taking TaskSnapshot when bitmap is provided. if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) { // Can block, so call before acquiring the lock. - TaskSnapshot snapshot = - mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false); + snapshot = mActivityTaskManagerInternal.getTaskSnapshotBlocking( + taskId, false /* isLowResolution */, + TaskSnapshot.REFERENCE_CONTENT_SUGGESTION); if (snapshot != null) { snapshotBuffer = snapshot.getHardwareBuffer(); ColorSpace colorSpace = snapshot.getColorSpace(); @@ -185,6 +187,9 @@ public class ContentSuggestionsManagerService extends } } } + if (snapshot != null) { + snapshot.removeReference(TaskSnapshot.REFERENCE_CONTENT_SUGGESTION); + } } @Override diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 05d07ae761c1..485bf31dfb64 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -1523,7 +1523,7 @@ public class BinaryTransparencyService extends SystemService { @Override public void onApexStaged(ApexStagedEvent event) throws RemoteException { Slog.d(TAG, "A new APEX has been staged for update. There are currently " - + event.stagedApexModuleNames.length + " APEX(s) staged for update. " + + event.stagedApexInfos.length + " APEX(s) staged for update. " + "Scheduling measurement..."); UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext, BinaryTransparencyService.this); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 744227760d95..39ac5150c7f1 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -422,9 +422,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private int[] mSimultaneousCellularCallingSubIds = {}; private int[] mECBMReason; - private boolean[] mECBMStarted; + private long[] mECBMDuration; private int[] mSCBMReason; - private boolean[] mSCBMStarted; + private long[] mSCBMDuration; private boolean[] mCarrierRoamingNtnMode = null; private boolean[] mCarrierRoamingNtnEligible = null; @@ -724,9 +724,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones); mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones); mECBMReason = copyOf(mECBMReason, mNumPhones); - mECBMStarted = copyOf(mECBMStarted, mNumPhones); + mECBMDuration = copyOf(mECBMDuration, mNumPhones); mSCBMReason = copyOf(mSCBMReason, mNumPhones); - mSCBMStarted = copyOf(mSCBMStarted, mNumPhones); + mSCBMDuration = copyOf(mSCBMDuration, mNumPhones); mCarrierRoamingNtnMode = copyOf(mCarrierRoamingNtnMode, mNumPhones); mCarrierRoamingNtnEligible = copyOf(mCarrierRoamingNtnEligible, mNumPhones); // ds -> ss switch. @@ -784,9 +784,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0])); mCarrierServiceStates.add(i, new Pair<>(null, Process.INVALID_UID)); mECBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN; - mECBMStarted[i] = false; + mECBMDuration[i] = 0; mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN; - mSCBMStarted[i] = false; + mSCBMDuration[i] = 0; mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; } @@ -859,9 +859,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCarrierPrivilegeStates = new ArrayList<>(); mCarrierServiceStates = new ArrayList<>(); mECBMReason = new int[numPhones]; - mECBMStarted = new boolean[numPhones]; + mECBMDuration = new long[numPhones]; mSCBMReason = new int[numPhones]; - mSCBMStarted = new boolean[numPhones]; + mSCBMDuration = new long[numPhones]; mCarrierRoamingNtnMode = new boolean[numPhones]; mCarrierRoamingNtnEligible = new boolean[numPhones]; @@ -904,9 +904,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0])); mCarrierServiceStates.add(i, new Pair<>(null, Process.INVALID_UID)); mECBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN; - mECBMStarted[i] = false; + mECBMDuration[i] = 0; mSCBMReason[i] = TelephonyManager.STOP_REASON_UNKNOWN; - mSCBMStarted[i] = false; + mSCBMDuration[i] = 0; mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; } @@ -1493,24 +1493,24 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if (events.contains(TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) { try { - boolean ecbmStarted = mECBMStarted[r.phoneId]; - if (ecbmStarted) { - r.callback.onCallBackModeStarted( - TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL); + if (mECBMDuration[r.phoneId] != 0) { + r.callback.onCallbackModeStarted( + TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, + mECBMDuration[r.phoneId], r.subId); } else { - r.callback.onCallBackModeStopped( + r.callback.onCallbackModeStopped( TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL, - mECBMReason[r.phoneId]); + mECBMReason[r.phoneId], r.subId); } - boolean scbmStarted = mSCBMStarted[r.phoneId]; - if (scbmStarted) { - r.callback.onCallBackModeStarted( - TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS); + if (mSCBMReason[r.phoneId] != 0) { + r.callback.onCallbackModeStarted( + TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS, + mSCBMDuration[r.phoneId], r.subId); } else { - r.callback.onCallBackModeStopped( + r.callback.onCallbackModeStopped( TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS, - mSCBMReason[r.phoneId]); + mSCBMReason[r.phoneId], r.subId); } } catch (RemoteException ex) { remove(r.binder); @@ -3457,10 +3457,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyCallbackModeStarted(int phoneId, int subId, int type) { - if (!checkNotifyPermission("notifyCallbackModeStarted()")) { - return; - } + public void notifyCallbackModeStarted(int phoneId, int subId, int type, long durationMillis) { + if (!checkNotifyPermission("notifyCallbackModeStarted()")) return; + if (VDBG) { log("notifyCallbackModeStarted: phoneId=" + phoneId + ", subId=" + subId + ", type=" + type); @@ -3468,9 +3467,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { if (validatePhoneId(phoneId)) { if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) { - mECBMStarted[phoneId] = true; + mECBMDuration[phoneId] = durationMillis; } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) { - mSCBMStarted[phoneId] = true; + mSCBMDuration[phoneId] = durationMillis; } } for (Record r : mRecords) { @@ -3478,7 +3477,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) { try { - r.callback.onCallBackModeStarted(type); + r.callback.onCallbackModeStarted(type, durationMillis, subId); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -3489,10 +3488,41 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason) { - if (!checkNotifyPermission("notifyCallbackModeStopped()")) { - return; + public void notifyCallbackModeRestarted(int phoneId, int subId, int type, + long durationMillis) { + if (!checkNotifyPermission("notifyCallbackModeRestarted()")) return; + + if (VDBG) { + log("notifyCallbackModeRestarted: phoneId=" + phoneId + ", subId=" + subId + + ", type=" + type); } + synchronized (mRecords) { + if (validatePhoneId(phoneId)) { + if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) { + mECBMDuration[phoneId] = durationMillis; + } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) { + mSCBMDuration[phoneId] = durationMillis; + } + } + for (Record r : mRecords) { + // Send to all listeners regardless of subscription + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) { + try { + r.callback.onCallbackModeRestarted(type, durationMillis, subId); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + } + handleRemoveListLocked(); + } + + @Override + public void notifyCallbackModeStopped(int phoneId, int subId, int type, int reason) { + if (!checkNotifyPermission("notifyCallbackModeStopped()")) return; + if (VDBG) { log("notifyCallbackModeStopped: phoneId=" + phoneId + ", subId=" + subId + ", type=" + type + ", reason=" + reason); @@ -3500,11 +3530,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { synchronized (mRecords) { if (validatePhoneId(phoneId)) { if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL) { - mECBMStarted[phoneId] = false; mECBMReason[phoneId] = reason; + mECBMDuration[phoneId] = 0; } else if (type == TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS) { - mSCBMStarted[phoneId] = false; mSCBMReason[phoneId] = reason; + mSCBMDuration[phoneId] = 0; } } for (Record r : mRecords) { @@ -3512,7 +3542,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (r.matchTelephonyCallbackEvent( TelephonyCallback.EVENT_EMERGENCY_CALLBACK_MODE_CHANGED)) { try { - r.callback.onCallBackModeStopped(type, reason); + r.callback.onCallbackModeStopped(type, reason, subId); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -3662,9 +3692,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mPhysicalChannelConfigs=" + mPhysicalChannelConfigs.get(i)); pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i)); pw.println("mECBMReason=" + mECBMReason[i]); - pw.println("mECBMStarted=" + mECBMStarted[i]); + pw.println("mECBMDuration=" + mECBMDuration[i]); pw.println("mSCBMReason=" + mSCBMReason[i]); - pw.println("mSCBMStarted=" + mSCBMStarted[i]); + pw.println("mSCBMDuration=" + mSCBMDuration[i]); pw.println("mCarrierRoamingNtnMode=" + mCarrierRoamingNtnMode[i]); pw.println("mCarrierRoamingNtnEligible=" + mCarrierRoamingNtnEligible[i]); diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index f8857d3e152a..d13dd2f2e1fc 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -443,6 +443,11 @@ final class UiModeManagerService extends SystemService { mDreamsDisabledByAmbientModeSuppression = disabledByAmbientModeSuppression; } + @VisibleForTesting + void setCurrentUser(int currentUserId) { + mCurrentUser = currentUserId; + } + @Override public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { mCurrentUser = to.getUserIdentifier(); @@ -864,9 +869,9 @@ final class UiModeManagerService extends SystemService { throw new IllegalArgumentException("Unknown mode: " + mode); } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { synchronized (mLock) { @@ -929,7 +934,7 @@ final class UiModeManagerService extends SystemService { @AttentionModeThemeOverlayType int attentionModeThemeOverlayType) { setAttentionModeThemeOverlay_enforcePermission(); - enforceCurrentUserIfVisibleBackgroundEnabled(UserHandle.getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mAttentionModeThemeOverlay != attentionModeThemeOverlayType) { @@ -1020,16 +1025,16 @@ final class UiModeManagerService extends SystemService { return false; } final int user = Binder.getCallingUserHandle().getIdentifier(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); - if (user != mCurrentUser && getContext().checkCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS) != PackageManager.PERMISSION_GRANTED) { Slog.e(TAG, "Target user is not current user," + " INTERACT_ACROSS_USERS permission is required"); return false; - } + + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + // Store the last requested bedtime night mode state so that we don't need to notify // anyone if the user decides to switch to the night mode to bedtime. if (modeCustomType == MODE_NIGHT_CUSTOM_TYPE_BEDTIME) { @@ -1078,9 +1083,10 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Set custom time start, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1108,9 +1114,10 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Set custom time end, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - final int user = UserHandle.getCallingUserId(); - enforceCurrentUserIfVisibleBackgroundEnabled(user); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); + + final int user = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { LocalTime newTime = LocalTime.ofNanoOfDay(time * 1000); @@ -1131,7 +1138,7 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mProjectionHolders == null) { @@ -1177,7 +1184,7 @@ final class UiModeManagerService extends SystemService { assertLegit(callingPackage); assertSingleProjectionType(projectionType); enforceProjectionTypePermissions(projectionType); - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); return releaseProjectionUnchecked(projectionType, callingPackage); } @@ -1219,7 +1226,7 @@ final class UiModeManagerService extends SystemService { return; } - enforceCurrentUserIfVisibleBackgroundEnabled(getCallingUserId()); + enforceCurrentUserIfVisibleBackgroundEnabled(mCurrentUser); synchronized (mLock) { if (mProjectionListeners == null) { diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 12e8c57228d6..947f6b73d32a 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -48,7 +48,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; -import android.net.vcn.Flags; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; @@ -447,22 +446,16 @@ public class VcnManagementService extends IVcnManagementService.Stub { } final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); + final UserManager userManager = mContext.getSystemService(UserManager.class); - if (Flags.enforceMainUser()) { - final UserManager userManager = mContext.getSystemService(UserManager.class); - - Binder.withCleanCallingIdentity( - () -> { - if (!Objects.equals(userManager.getMainUser(), userHandle)) { - throw new SecurityException( - "VcnManagementService can only be used by callers running as" - + " the main user"); - } - }); - } else if (!userHandle.isSystem()) { - throw new SecurityException( - "VcnManagementService can only be used by callers running as the primary user"); - } + Binder.withCleanCallingIdentity( + () -> { + if (!Objects.equals(userManager.getMainUser(), userHandle)) { + throw new SecurityException( + "VcnManagementService can only be used by callers running as" + + " the main user"); + } + }); } private void enforceCallingUserAndCarrierPrivilege( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3bfbc5557024..3f540ad43da1 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -7227,7 +7227,7 @@ public final class ActiveServices { mAm.mBatteryStatsService.noteServiceStopLaunch(sr.appInfo.uid, sr.name.getPackageName(), sr.name.getClassName()); if (sr.app != app && sr.app != null && !sr.app.isPersistent()) { - mAm.mProcessStateController.stopService(psr, sr); + mAm.mProcessStateController.stopService(sr.app.mServices, sr); sr.app.mServices.updateBoundClientUids(); } sr.setProcess(null, null, 0, null); diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index f5a297bfd4f5..65a2c187f1c8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -246,7 +246,7 @@ final class ActivityManagerConstants extends ContentObserver { static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000; - private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false; + private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = Flags.oomadjusterCachedAppTiers(); private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000; /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f42f91ed5ce1..7f1d912d9a79 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -441,6 +441,7 @@ import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; +import com.android.server.crashrecovery.CrashRecoveryAdaptor; import com.android.server.crashrecovery.CrashRecoveryHelper; import com.android.server.criticalevents.CriticalEventLog; import com.android.server.firewall.IntentFirewall; @@ -2210,7 +2211,7 @@ public class ActivityManagerService extends IActivityManager.Stub mService.mBroadcastController.startBroadcastObservers(); } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { if (!refactorCrashrecovery()) { - mService.mPackageWatchdog.onPackagesReady(); + CrashRecoveryAdaptor.packageWatchdogOnPackagesReady(mService.mPackageWatchdog); } else { mService.mCrashRecoveryHelper.registerConnectivityModuleHealthListener(); } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 4ac42ecd8f50..592d89eca285 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -833,6 +833,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub return sService; } + /** + * Override the {@link IBatteryStats} service, for testing. + */ + @VisibleForTesting + public static void overrideService(IBatteryStats service) { + sService = service; + } + @Override public int getServiceType() { return ServiceType.BATTERY_STATS; diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 221938a3ff26..6e09a84e0f8c 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -90,7 +90,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; -import com.android.server.RescueParty; +import com.android.server.crashrecovery.CrashRecoveryAdaptor; import com.android.server.pm.UserManagerInternal; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.sdksandbox.SdkSandboxManagerLocal; @@ -1382,7 +1382,7 @@ public class ContentProviderHelper { mService.mOomAdjuster.initSettings(); // Now that the settings provider is published we can consider sending in a rescue party. - RescueParty.onSettingsProviderPublished(mService.mContext); + CrashRecoveryAdaptor.rescuePartyOnSettingsProviderPublished(mService.mContext); } /** diff --git a/services/core/java/com/android/server/am/ProcessStateController.java b/services/core/java/com/android/server/am/ProcessStateController.java index 428df2361f0d..01468c640f6c 100644 --- a/services/core/java/com/android/server/am/ProcessStateController.java +++ b/services/core/java/com/android/server/am/ProcessStateController.java @@ -395,10 +395,9 @@ public class ProcessStateController { */ public void removeProviderConnection(@NonNull ProcessRecord client, ContentProviderConnection cpc) { - client.mProviders.addProviderConnection(cpc); + client.mProviders.removeProviderConnection(cpc); } - /********************** Content Provider State Events **********************/ /*************************** Service State Events **************************/ /** * Note that a process has started hosting a service. diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index adf0e640f6bf..d67fea09f945 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -225,3 +225,11 @@ flag { description: "Migrate OomAdjuster pulled device state to a push model" bug: "302575389" } + +flag { + name: "oomadjuster_cached_app_tiers" + namespace: "system_performance" + is_fixed_read_only: true + description: "Assign cached oom_score_adj in tiers." + bug: "369893532" +} diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 60dbf3f21587..dbdc614bdc9e 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -30,6 +30,7 @@ import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; +import android.content.AttributionSource; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -306,14 +307,13 @@ public class AudioDeviceBroker { * @param on true to enable speakerphone * @param eventSource for logging purposes */ - /*package*/ void setSpeakerphoneOn( - IBinder cb, int uid, boolean on, boolean isPrivileged, String eventSource) { - + /*package*/ void setSpeakerphoneOn(IBinder cb, @NonNull AttributionSource attributionSource, + boolean on, boolean isPrivileged, String eventSource) { if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + uid); + Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + attributionSource.getUid()); } - postSetCommunicationDeviceForClient(new CommunicationDeviceInfo( - cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""), + postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(cb, attributionSource, + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""), on, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged)); } @@ -332,16 +332,18 @@ public class AudioDeviceBroker { * @param eventSource for logging purposes * @return false if there is no device and no communication client */ - /*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device, - boolean isPrivileged, String eventSource) { - + /*package*/ boolean setCommunicationDevice(IBinder cb, + @NonNull AttributionSource attributionSource, AudioDeviceInfo device, + boolean isPrivileged, String eventSource) { if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "setCommunicationDevice, device: " + device + ", uid: " + uid); + Log.v(TAG, "setCommunicationDevice, device: " + device + + ", uid: " + attributionSource.getUid()); } if (device == null) { synchronized (mDeviceStateLock) { - CommunicationRouteClient client = getCommunicationRouteClientForUid(uid); + CommunicationRouteClient client = + getCommunicationRouteClientForUid(attributionSource.getUid()); if (client == null) { return false; } @@ -351,7 +353,8 @@ public class AudioDeviceBroker { mCommunicationDeviceUpdateCount++; AudioDeviceAttributes deviceAttr = (device != null) ? new AudioDeviceAttributes(device) : null; - CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, uid, deviceAttr, + CommunicationDeviceInfo deviceInfo = + new CommunicationDeviceInfo(cb, attributionSource, deviceAttr, device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged); postSetCommunicationDeviceForClient(deviceInfo); } @@ -369,7 +372,8 @@ public class AudioDeviceBroker { Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo); } if (!deviceInfo.mOn) { - CommunicationRouteClient client = getCommunicationRouteClientForUid(deviceInfo.mUid); + CommunicationRouteClient client = + getCommunicationRouteClientForUid(deviceInfo.mAttributionSource.getUid()); if (client == null || (deviceInfo.mDevice != null && !deviceInfo.mDevice.equals(client.getDevice()))) { return; @@ -377,53 +381,60 @@ public class AudioDeviceBroker { } AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null; - setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mUid, device, - deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, deviceInfo.mEventSource); + setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mAttributionSource, + device, deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, + deviceInfo.mEventSource); } /** * Indicates if a Bluetooth SCO activation request owner is controlling * the SCO audio state itself or not. - * @param uid the UID of the SOC request owner app + * @param attributionSource the AttributionSource of the SCO request owner app * @return true if we should control SCO audio state, false otherwise */ - private boolean shouldStartScoForUid(int uid) { + private boolean shouldStartScoForAttributionSource(AttributionSource attributionSource) { + if (attributionSource == null) { + return true; + } + int uid = attributionSource.getUid(); return !(UserHandle.isSameApp(uid, Process.BLUETOOTH_UID) || UserHandle.isSameApp(uid, Process.PHONE_UID) - || UserHandle.isSameApp(uid, Process.SYSTEM_UID)); + || (UserHandle.isSameApp(uid, Process.SYSTEM_UID) + && "com.android.server.telecom".equals(attributionSource.getPackageName()))); } @GuardedBy("mDeviceStateLock") /*package*/ void setCommunicationRouteForClient( - IBinder cb, int uid, AudioDeviceAttributes device, - int scoAudioMode, boolean isPrivileged, String eventSource) { - + IBinder cb, @NonNull AttributionSource attributionSource, AudioDeviceAttributes device, + int scoAudioMode, boolean isPrivileged, String eventSource) { if (AudioService.DEBUG_COMM_RTE) { Log.v(TAG, "setCommunicationRouteForClient: device: " + device + ", eventSource: " + eventSource); } AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( - "setCommunicationRouteForClient for uid: " + uid + "setCommunicationRouteForClient for uid: " + + attributionSource.getUid() + " device: " + device + " isPrivileged: " + isPrivileged + " from API: " + eventSource)).printLog(TAG)); - final int previousBtScoRequesterUid = bluetoothScoRequestOwnerUid(); + final AttributionSource previousBtScoRequesterAS = + bluetoothScoRequestOwnerAttributionSource(); CommunicationRouteClient client; // Save previous client route in case of failure to start BT SCO audio AudioDeviceAttributes prevClientDevice = null; boolean prevPrivileged = false; - client = getCommunicationRouteClientForUid(uid); + client = getCommunicationRouteClientForUid(attributionSource.getUid()); if (client != null) { prevClientDevice = client.getDevice(); prevPrivileged = client.isPrivileged(); } if (device != null) { - client = addCommunicationRouteClient(cb, uid, device, isPrivileged); + client = addCommunicationRouteClient(cb, attributionSource, device, isPrivileged); if (client == null) { Log.w(TAG, "setCommunicationRouteForClient: could not add client for uid: " - + uid + " and device: " + device); + + attributionSource.getUid() + " and device: " + device); } } else { client = removeCommunicationRouteClient(cb, true); @@ -431,22 +442,23 @@ public class AudioDeviceBroker { if (client == null) { return; } - final int btScoRequesterUid = bluetoothScoRequestOwnerUid(); - final boolean isBtScoRequested = btScoRequesterUid != -1; - final boolean wasBtScoRequested = previousBtScoRequesterUid != -1; + final AttributionSource btScoRequesterAS = bluetoothScoRequestOwnerAttributionSource(); + final boolean isBtScoRequested = btScoRequesterAS != null; + final boolean wasBtScoRequested = previousBtScoRequesterAS != null; if (mScoManagedByAudio) { if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive() || !mBtHelper.isBluetoothScoRequestedInternally())) { boolean scoStarted = false; - if (shouldStartScoForUid(btScoRequesterUid)) { + if (shouldStartScoForAttributionSource(btScoRequesterAS)) { scoStarted = mBtHelper.startBluetoothSco(scoAudioMode, eventSource); if (!scoStarted) { Log.w(TAG, "setCommunicationRouteForClient: " - + "failure to start BT SCO for uid: " + uid); + + "failure to start BT SCO for uid: " + attributionSource.getUid()); // clean up or restore previous client selection if (prevClientDevice != null) { - addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged); + addCommunicationRouteClient(cb, attributionSource, + prevClientDevice, prevPrivileged); } else { removeCommunicationRouteClient(cb, true); } @@ -459,7 +471,7 @@ public class AudioDeviceBroker { setBluetoothScoOn(true, "setCommunicationRouteForClient"); } } else if (!isBtScoRequested && wasBtScoRequested) { - if (shouldStartScoForUid(previousBtScoRequesterUid)) { + if (shouldStartScoForAttributionSource(previousBtScoRequesterAS)) { mBtHelper.stopBluetoothSco(eventSource); } setBluetoothScoOn(false, "setCommunicationRouteForClient"); @@ -469,10 +481,11 @@ public class AudioDeviceBroker { || !mBtHelper.isBluetoothScoRequestedInternally())) { if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) { Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: " - + uid); + + attributionSource.getUid()); // clean up or restore previous client selection if (prevClientDevice != null) { - addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged); + addCommunicationRouteClient(cb, attributionSource, + prevClientDevice, prevPrivileged); } else { removeCommunicationRouteClient(cb, true); } @@ -583,7 +596,7 @@ public class AudioDeviceBroker { // Cancelling the route for this client will remove it from the stack and update // the communication route. CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo( - crc.getBinder(), crc.getUid(), device, false, + crc.getBinder(), crc.getAttributionSource(), device, false, BtHelper.SCO_MODE_UNDEFINED, "onCheckCommunicationDeviceRemoval", crc.isPrivileged()); postSetCommunicationDeviceForClient(deviceInfo); @@ -619,12 +632,11 @@ public class AudioDeviceBroker { @GuardedBy("mDeviceStateLock") /*package*/ void updateCommunicationRouteClientState( CommunicationRouteClient client, boolean wasActive) { - int btScoRequesterUid = bluetoothScoRequestOwnerUid(); client.setPlaybackActive(mAudioService.isPlaybackActiveForUid(client.getUid())); client.setRecordingActive(mAudioService.isRecordingActiveForUid(client.getUid())); if (wasActive != client.isActive()) { - postUpdateCommunicationRouteClient( - btScoRequesterUid, "updateCommunicationRouteClientState"); + postUpdateCommunicationRouteClient(bluetoothScoRequestOwnerAttributionSource(), + "updateCommunicationRouteClientState"); } } @@ -810,21 +822,26 @@ public class AudioDeviceBroker { } /** - * Helper method on top of isBluetoothScoRequested() returning the UID of the - * BT SCO route request owner of -1 if SCO is not requested. - * @return the UID of the BT SCO route request owner of -1 if SCO is not requested. + * Helper method on top of isBluetoothScoRequested() returning the Attribution Source of the + * BT SCO route request owner or null if SCO is not requested. + * @return the AttributionSource of the BT SCO route request owner of null. */ @GuardedBy("mDeviceStateLock") - /*package*/ int bluetoothScoRequestOwnerUid() { + /*package*/ @Nullable AttributionSource bluetoothScoRequestOwnerAttributionSource() { if (!isBluetoothScoRequested()) { - return -1; + return null; } CommunicationRouteClient crc = topCommunicationRouteClient(); if (crc == null) { - return -1; + return null; } - return crc.getUid(); + return crc.getAttributionSource(); + } + + private static int safeUidFromAttributionSource(AttributionSource attributionSource) { + return (attributionSource != null) ? attributionSource.getUid() : -1; } + /** * Helper method on top of isDeviceRequestedForCommunication() indicating if * Bluetooth LE Audio communication device is currently requested or not. @@ -1222,14 +1239,15 @@ public class AudioDeviceBroker { @GuardedBy("mDeviceStateLock") /*package*/ void setBluetoothScoOn(boolean on, String eventSource) { synchronized (mBluetoothAudioStateLock) { - int btScoRequesterUId = bluetoothScoRequestOwnerUid(); + AttributionSource btScoRequesterAS = bluetoothScoRequestOwnerAttributionSource(); Log.i(TAG, "setBluetoothScoOn: " + on + ", mBluetoothScoOn: " - + mBluetoothScoOn + ", btScoRequesterUId: " + btScoRequesterUId + + mBluetoothScoOn + ", btScoRequesterUId: " + + safeUidFromAttributionSource(btScoRequesterAS) + ", from: " + eventSource); mBluetoothScoOn = on; updateAudioHalBluetoothState(); if (!mScoManagedByAudio) { - postUpdateCommunicationRouteClient(btScoRequesterUId, eventSource); + postUpdateCommunicationRouteClient(btScoRequesterAS, eventSource); } } } @@ -1332,25 +1350,26 @@ public class AudioDeviceBroker { sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info); } - /*package*/ void startBluetoothScoForClient(IBinder cb, int uid, int scoAudioMode, - boolean isPrivileged, @NonNull String eventSource) { - + /*package*/ void startBluetoothScoForClient(IBinder cb, + @NonNull AttributionSource attributionSource, int scoAudioMode, boolean isPrivileged, + @NonNull String eventSource) { if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "startBluetoothScoForClient, uid: " + uid); + Log.v(TAG, "startBluetoothScoForClient, uid: " + attributionSource.getUid()); } - postSetCommunicationDeviceForClient(new CommunicationDeviceInfo( - cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""), + postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(cb, attributionSource, + new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""), true, scoAudioMode, eventSource, isPrivileged)); } - /*package*/ void stopBluetoothScoForClient( - IBinder cb, int uid, boolean isPrivileged, @NonNull String eventSource) { - + /*package*/ void stopBluetoothScoForClient(IBinder cb, + @NonNull AttributionSource attributionSource, boolean isPrivileged, + @NonNull String eventSource) { if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "stopBluetoothScoForClient, uid: " + uid); + Log.v(TAG, "stopBluetoothScoForClient, uid: " + attributionSource.getUid()); } postSetCommunicationDeviceForClient(new CommunicationDeviceInfo( - cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""), + cb, attributionSource, new AudioDeviceAttributes( + AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""), false, BtHelper.SCO_MODE_UNDEFINED, eventSource, isPrivileged)); } @@ -1566,10 +1585,21 @@ public class AudioDeviceBroker { sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client); } + private static final class UpdateCommRouteClientInfo { + @NonNull public final AttributionSource attributionSource; + @NonNull public final String eventSource; + + UpdateCommRouteClientInfo(@NonNull AttributionSource attributionSource, + @NonNull String eventSource) { + this.attributionSource = attributionSource; + this.eventSource = eventSource; + } + } + /*package*/ void postUpdateCommunicationRouteClient( - int btScoRequesterUid, String eventSource) { - sendILMsgNoDelay(MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, - btScoRequesterUid, eventSource); + AttributionSource attributionSource, String eventSource) { + sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, + new UpdateCommRouteClientInfo(attributionSource, eventSource)); } /*package*/ void postSetCommunicationDeviceForClient(CommunicationDeviceInfo info) { @@ -1600,18 +1630,18 @@ public class AudioDeviceBroker { /*package*/ static final class CommunicationDeviceInfo { final @NonNull IBinder mCb; // Identifies the requesting client for death handler - final int mUid; // Requester UID + final @NonNull AttributionSource mAttributionSource; // Requester attribution source final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset. final boolean mOn; // true if setting, false if resetting final int mScoAudioMode; // only used for SCO: requested audio mode final boolean mIsPrivileged; // true if the client app has MODIFY_PHONE_STATE permission final @NonNull String mEventSource; // caller identifier for logging - CommunicationDeviceInfo(@NonNull IBinder cb, int uid, + CommunicationDeviceInfo(@NonNull IBinder cb, @NonNull AttributionSource attributionSource, @Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode, @NonNull String eventSource, boolean isPrivileged) { mCb = cb; - mUid = uid; + mAttributionSource = attributionSource; mDevice = device; mOn = on; mScoAudioMode = scoAudioMode; @@ -1633,19 +1663,19 @@ public class AudioDeviceBroker { } return mCb.equals(((CommunicationDeviceInfo) o).mCb) - && mUid == ((CommunicationDeviceInfo) o).mUid; + && mAttributionSource.equals(((CommunicationDeviceInfo) o).mAttributionSource); } @Override public int hashCode() { // only hashing on the fields used in equals() - return Objects.hash(mCb.hashCode(), mUid); + return Objects.hash(mCb.hashCode(), mAttributionSource); } @Override public String toString() { return "CommunicationDeviceInfo mCb=" + mCb.toString() - + " mUid=" + mUid + + " mAttributionSource=" + mAttributionSource.toString() + " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]" + " mOn=" + mOn + " mScoAudioMode=" + mScoAudioMode @@ -1929,7 +1959,8 @@ public class AudioDeviceBroker { || btInfo.mProfile == BluetoothProfile.HEARING_AID || (mScoManagedByAudio && btInfo.mProfile == BluetoothProfile.HEADSET)) { - onUpdateCommunicationRouteClient(bluetoothScoRequestOwnerUid(), + onUpdateCommunicationRouteClient( + bluetoothScoRequestOwnerAttributionSource(), "setBluetoothActiveDevice"); } } @@ -1998,11 +2029,11 @@ public class AudioDeviceBroker { case MSG_IL_SET_MODE_OWNER: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { - int btScoRequesterUid = bluetoothScoRequestOwnerUid(); mAudioModeOwner = (AudioModeInfo) msg.obj; if (mAudioModeOwner.mMode != AudioSystem.MODE_RINGTONE) { onUpdateCommunicationRouteClient( - btScoRequesterUid, "setNewModeOwner"); + bluetoothScoRequestOwnerAttributionSource(), + "setNewModeOwner"); } } } @@ -2029,10 +2060,12 @@ public class AudioDeviceBroker { } break; - case MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT: + case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { - onUpdateCommunicationRouteClient(msg.arg1, (String) msg.obj); + UpdateCommRouteClientInfo info = (UpdateCommRouteClientInfo) msg.obj; + onUpdateCommunicationRouteClient( + info.attributionSource, info.eventSource); } } break; @@ -2182,7 +2215,7 @@ public class AudioDeviceBroker { private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36; private static final int MSG_L_SET_COMMUNICATION_DEVICE_FOR_CLIENT = 42; - private static final int MSG_IL_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43; + private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43; private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45; // @@ -2394,20 +2427,20 @@ public class AudioDeviceBroker { private class CommunicationRouteClient implements IBinder.DeathRecipient { private final IBinder mCb; - private final int mUid; + @NonNull private final AttributionSource mAttributionSource; private final boolean mIsPrivileged; private AudioDeviceAttributes mDevice; private boolean mPlaybackActive; private boolean mRecordingActive; - CommunicationRouteClient(IBinder cb, int uid, AudioDeviceAttributes device, - boolean isPrivileged) { + CommunicationRouteClient(IBinder cb, @NonNull AttributionSource attributionSource, + AudioDeviceAttributes device, boolean isPrivileged) { mCb = cb; - mUid = uid; + mAttributionSource = attributionSource; mDevice = device; mIsPrivileged = isPrivileged; - mPlaybackActive = mAudioService.isPlaybackActiveForUid(uid); - mRecordingActive = mAudioService.isRecordingActiveForUid(uid); + mPlaybackActive = mAudioService.isPlaybackActiveForUid(attributionSource.getUid()); + mRecordingActive = mAudioService.isRecordingActiveForUid(attributionSource.getUid()); } public boolean registerDeathRecipient() { @@ -2438,8 +2471,12 @@ public class AudioDeviceBroker { return mCb; } + @NonNull AttributionSource getAttributionSource() { + return mAttributionSource; + } + int getUid() { - return mUid; + return mAttributionSource.getUid(); } boolean isPrivileged() { @@ -2464,7 +2501,7 @@ public class AudioDeviceBroker { @Override public String toString() { - return "[CommunicationRouteClient: mUid: " + mUid + return "[CommunicationRouteClient: mAttributionSource: " + mAttributionSource + " mDevice: " + mDevice.toString() + " mIsPrivileged: " + mIsPrivileged + " mPlaybackActive: " + mPlaybackActive @@ -2479,8 +2516,8 @@ public class AudioDeviceBroker { return; } Log.w(TAG, "Communication client died"); - setCommunicationRouteForClient(client.getBinder(), client.getUid(), null, - BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(), + setCommunicationRouteForClient(client.getBinder(), client.getAttributionSource(), + null, BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(), "onCommunicationRouteClientDied"); } @@ -2561,21 +2598,22 @@ public class AudioDeviceBroker { // @GuardedBy("mSetModeLock") @GuardedBy("mDeviceStateLock") private void onUpdateCommunicationRouteClient( - int previousBtScoRequesterUid, String eventSource) { + @Nullable AttributionSource previousBtScoRequesterAS, String eventSource) { CommunicationRouteClient crc = topCommunicationRouteClient(); if (AudioService.DEBUG_COMM_RTE) { Log.v(TAG, "onUpdateCommunicationRouteClient, crc: " + crc - + " previousBtScoRequesterUid: " + previousBtScoRequesterUid + + " previous BT SCO Requester UID: " + + safeUidFromAttributionSource(previousBtScoRequesterAS) + " eventSource: " + eventSource); } if (crc != null) { - setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(), - BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource); + setCommunicationRouteForClient(crc.getBinder(), crc.getAttributionSource(), + crc.getDevice(), BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource); } else { - boolean wasScoRequested = previousBtScoRequesterUid != -1; + boolean wasScoRequested = previousBtScoRequesterAS != null; if (!isBluetoothScoRequested() && wasScoRequested) { if (mScoManagedByAudio) { - if (shouldStartScoForUid(previousBtScoRequesterUid)) { + if (shouldStartScoForAttributionSource(previousBtScoRequesterAS)) { mBtHelper.stopBluetoothSco(eventSource); } setBluetoothScoOn(false, eventSource); @@ -2624,12 +2662,13 @@ public class AudioDeviceBroker { } @GuardedBy("mDeviceStateLock") - private CommunicationRouteClient addCommunicationRouteClient(IBinder cb, int uid, - AudioDeviceAttributes device, boolean isPrivileged) { + private CommunicationRouteClient addCommunicationRouteClient( + IBinder cb, @NonNull AttributionSource attributionSource, AudioDeviceAttributes device, + boolean isPrivileged) { // always insert new request at first position removeCommunicationRouteClient(cb, true); CommunicationRouteClient client = - new CommunicationRouteClient(cb, uid, device, isPrivileged); + new CommunicationRouteClient(cb, attributionSource, device, isPrivileged); if (client.registerDeathRecipient()) { mCommunicationRouteClients.add(0, client); if (!client.isActive()) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index fdf7dec31cad..37a2fba8fcb5 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -69,6 +69,7 @@ import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume; import static com.android.media.audio.Flags.equalScoLeaVcIndexRange; import static com.android.media.audio.Flags.replaceStreamBtSco; import static com.android.media.audio.Flags.ringerModeAffectsAlarm; +import static com.android.media.audio.Flags.ringMyCar; import static com.android.media.audio.Flags.setStreamVolumeOrder; import static com.android.media.audio.Flags.vgsVssSyncMuteOrder; import static com.android.server.audio.SoundDoseHelper.ACTION_CHECK_MUSIC_ACTIVE; @@ -761,7 +762,7 @@ public class AudioService extends IAudioService.Stub /** Streams that can be muted by system. Do not resolve to aliases when checking. * @see System#MUTE_STREAMS_AFFECTED */ - private int mMuteAffectedStreams; + protected int mMuteAffectedStreams; /** Streams that can be muted by user. Do not resolve to aliases when checking. * @see System#MUTE_STREAMS_AFFECTED */ @@ -1465,7 +1466,8 @@ public class AudioService extends IAudioService.Stub mPlaybackMonitor = new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM], - device -> onMuteAwaitConnectionTimeout(device)); + device -> onMuteAwaitConnectionTimeout(device), + stream -> isStreamMute(stream)); mPlaybackMonitor.registerPlaybackCallback(mPlaybackActivityMonitor, true); mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); @@ -4846,6 +4848,8 @@ public class AudioService extends IAudioService.Stub + replaceStreamBtSco()); pw.println("\tcom.android.media.audio.equalScoLeaVcIndexRange:" + equalScoLeaVcIndexRange()); + pw.println("\tcom.android.media.audio.ringMyCar:" + + ringMyCar()); } private void dumpAudioMode(PrintWriter pw) { @@ -6840,9 +6844,13 @@ public class AudioService extends IAudioService.Stub * @see AudioManager#setCommunicationDevice(int) * @see AudioManager#clearCommunicationDevice() */ - public boolean setCommunicationDevice(IBinder cb, int portId) { - final int uid = Binder.getCallingUid(); - final int pid = Binder.getCallingPid(); + public boolean setCommunicationDevice(IBinder cb, int portId, + @NonNull AttributionSource attributionSource) { + if (attributionSource == null) { + return false; + } + final int uid = attributionSource.getUid(); + final int pid = attributionSource.getPid(); AudioDeviceInfo device = null; if (portId != 0) { @@ -6892,7 +6900,8 @@ public class AudioService extends IAudioService.Stub == PackageManager.PERMISSION_GRANTED; final long ident = Binder.clearCallingIdentity(); try { - return mDeviceBroker.setCommunicationDevice(cb, uid, device, isPrivileged, eventSource); + return mDeviceBroker.setCommunicationDevice( + cb, attributionSource, device, isPrivileged, eventSource); } finally { Binder.restoreCallingIdentity(ident); } @@ -6934,7 +6943,11 @@ public class AudioService extends IAudioService.Stub } /** @see AudioManager#setSpeakerphoneOn(boolean) */ - public void setSpeakerphoneOn(IBinder cb, boolean on) { + public void setSpeakerphoneOn(IBinder cb, boolean on, + @NonNull AttributionSource attributionSource) { + if (attributionSource == null) { + return; + } if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { return; } @@ -6942,8 +6955,8 @@ public class AudioService extends IAudioService.Stub == PackageManager.PERMISSION_GRANTED; // for logging only - final int uid = Binder.getCallingUid(); - final int pid = Binder.getCallingPid(); + final int uid = attributionSource.getUid(); + final int pid = attributionSource.getPid(); final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) .append(") from u/pid:").append(uid).append("/") @@ -6958,7 +6971,7 @@ public class AudioService extends IAudioService.Stub final long ident = Binder.clearCallingIdentity(); try { - mDeviceBroker.setSpeakerphoneOn(cb, uid, on, isPrivileged, eventSource); + mDeviceBroker.setSpeakerphoneOn(cb, attributionSource, on, isPrivileged, eventSource); } finally { Binder.restoreCallingIdentity(ident); } @@ -7062,13 +7075,17 @@ public class AudioService extends IAudioService.Stub } /** @see AudioManager#startBluetoothSco() */ - public void startBluetoothSco(IBinder cb, int targetSdkVersion) { + public void startBluetoothSco(IBinder cb, int targetSdkVersion, + @NonNull AttributionSource attributionSource) { + if (attributionSource == null) { + return; + } if (!checkAudioSettingsPermission("startBluetoothSco()")) { return; } - final int uid = Binder.getCallingUid(); - final int pid = Binder.getCallingPid(); + final int uid = attributionSource.getUid(); + final int pid = attributionSource.getPid(); final int scoAudioMode = (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ? BtHelper.SCO_MODE_VIRTUAL_CALL : BtHelper.SCO_MODE_UNDEFINED; @@ -7083,18 +7100,22 @@ public class AudioService extends IAudioService.Stub .set(MediaMetrics.Property.SCO_AUDIO_MODE, BtHelper.scoAudioModeToString(scoAudioMode)) .record(); - startBluetoothScoInt(cb, uid, scoAudioMode, eventSource); + startBluetoothScoInt(cb, attributionSource, scoAudioMode, eventSource); } /** @see AudioManager#startBluetoothScoVirtualCall() */ - public void startBluetoothScoVirtualCall(IBinder cb) { + public void startBluetoothScoVirtualCall(IBinder cb, + @NonNull AttributionSource attributionSource) { + if (attributionSource == null) { + return; + } if (!checkAudioSettingsPermission("startBluetoothScoVirtualCall()")) { return; } - final int uid = Binder.getCallingUid(); - final int pid = Binder.getCallingPid(); + final int uid = attributionSource.getUid(); + final int pid = attributionSource.getPid(); final String eventSource = new StringBuilder("startBluetoothScoVirtualCall()") .append(") from u/pid:").append(uid).append("/") .append(pid).toString(); @@ -7106,10 +7127,11 @@ public class AudioService extends IAudioService.Stub .set(MediaMetrics.Property.SCO_AUDIO_MODE, BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL)) .record(); - startBluetoothScoInt(cb, uid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource); + startBluetoothScoInt(cb, attributionSource, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource); } - void startBluetoothScoInt(IBinder cb, int uid, int scoAudioMode, @NonNull String eventSource) { + void startBluetoothScoInt(IBinder cb, AttributionSource attributionSource, + int scoAudioMode, @NonNull String eventSource) { MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt") .set(MediaMetrics.Property.SCO_AUDIO_MODE, @@ -7125,7 +7147,7 @@ public class AudioService extends IAudioService.Stub final long ident = Binder.clearCallingIdentity(); try { mDeviceBroker.startBluetoothScoForClient( - cb, uid, scoAudioMode, isPrivileged, eventSource); + cb, attributionSource, scoAudioMode, isPrivileged, eventSource); } finally { Binder.restoreCallingIdentity(ident); } @@ -7133,13 +7155,17 @@ public class AudioService extends IAudioService.Stub } /** @see AudioManager#stopBluetoothSco() */ - public void stopBluetoothSco(IBinder cb){ + public void stopBluetoothSco(IBinder cb, + @NonNull AttributionSource attributionSource) { + if (attributionSource == null) { + return; + } if (!checkAudioSettingsPermission("stopBluetoothSco()") || !mSystemReady) { return; } - final int uid = Binder.getCallingUid(); - final int pid = Binder.getCallingPid(); + final int uid = attributionSource.getUid(); + final int pid = attributionSource.getPid(); final String eventSource = new StringBuilder("stopBluetoothSco()") .append(") from u/pid:").append(uid).append("/") .append(pid).toString(); @@ -7147,7 +7173,8 @@ public class AudioService extends IAudioService.Stub == PackageManager.PERMISSION_GRANTED; final long ident = Binder.clearCallingIdentity(); try { - mDeviceBroker.stopBluetoothScoForClient(cb, uid, isPrivileged, eventSource); + mDeviceBroker.stopBluetoothScoForClient( + cb, attributionSource, isPrivileged, eventSource); } finally { Binder.restoreCallingIdentity(ident); } @@ -8672,9 +8699,14 @@ public class AudioService extends IAudioService.Stub // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted. // This allows RX path muting by the audio HAL only when explicitly muted but not when // index is just set to 0 to repect BT requirements + boolean muted = false; if (mHasValidStreamType && isVssMuteBijective(mPublicStreamType) && getVssForStreamOrDefault(mPublicStreamType).isFullyMuted()) { - index = 0; + if (ringMyCar()) { + muted = true; + } else { + index = 0; + } } else if (isStreamBluetoothSco(mPublicStreamType) && index == 0) { index = 1; } @@ -8684,13 +8716,14 @@ public class AudioService extends IAudioService.Stub / getVssForStreamOrDefault(mPublicStreamType).getIndexStepFactor()); } + if (DEBUG_VOL) { Log.d(TAG, "setVolumeIndexInt(" + mAudioVolumeGroup.getId() + ", " + index + ", " - + device + ")"); + + muted + ", " + device + ")"); } // Set the volume index - mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device); + mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, muted, device); } @GuardedBy("AudioService.VolumeStreamState.class") @@ -9274,6 +9307,13 @@ public class AudioService extends IAudioService.Stub } } + /** + * Sends the new volume index on the given device to native. + * + * <p>Make sure the index is consistent with the muting state. When ringMyCar is enabled + * will send the non-zero index together with muted state. Otherwise, index 0 will be sent + * to native for signalising a muted stream. + **/ @GuardedBy("VolumeStreamState.class") private void setStreamVolumeIndex(int index, int device) { // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted. @@ -9288,18 +9328,19 @@ public class AudioService extends IAudioService.Stub / 10; } + boolean muted = ringMyCar() ? isFullyMuted() : false; if (DEBUG_VOL) { - Log.d(TAG, "setStreamVolumeIndexAS(" + mStreamType + ", " + index + ", " + device - + ")"); + Log.d(TAG, "setStreamVolumeIndexAS(streamType=" + mStreamType + ", index=" + index + + ", muted=" + muted + ", device=" + device + ")"); } - mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device); + mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, muted, device); } // must be called while synchronized VolumeStreamState.class @GuardedBy("VolumeStreamState.class") /*package*/ void applyDeviceVolume_syncVSS(int device) { int index; - if (isFullyMuted()) { + if (isFullyMuted() && !ringMyCar()) { index = 0; } else if (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device) @@ -9333,7 +9374,7 @@ public class AudioService extends IAudioService.Stub for (int i = 0; i < mIndexMap.size(); i++) { final int device = mIndexMap.keyAt(i); if (device != AudioSystem.DEVICE_OUT_DEFAULT) { - if (isFullyMuted()) { + if (isFullyMuted() && !ringMyCar()) { index = 0; } else if (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device) @@ -9368,7 +9409,7 @@ public class AudioService extends IAudioService.Stub } // apply default volume last: by convention , default device volume will be used // by audio policy manager if no explicit volume is present for a given device type - if (isFullyMuted()) { + if (isFullyMuted() && !ringMyCar()) { index = 0; } else { index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10; diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java index 5cabddea9c17..e86c34cab88a 100644 --- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java +++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java @@ -547,13 +547,14 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, * @param device * @return */ - public int setStreamVolumeIndexAS(int stream, int index, int device) { - return AudioSystem.setStreamVolumeIndexAS(stream, index, device); + public int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) { + return AudioSystem.setStreamVolumeIndexAS(stream, index, muted, device); } /** Same as {@link AudioSystem#setVolumeIndexForAttributes(AudioAttributes, int, int)} */ - public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) { - return AudioSystem.setVolumeIndexForAttributes(attributes, index, device); + public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, boolean muted, + int device) { + return AudioSystem.setVolumeIndexForAttributes(attributes, index, muted, device); } /** diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index a734e73d213b..b63b07f0453e 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -72,6 +72,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; +import java.util.function.Function; /** * Class to receive and dispatch updates from AudioSystem about recording configurations. @@ -160,18 +161,22 @@ public final class PlaybackActivityMonitor private final Context mContext; private int mSavedAlarmVolume = -1; + private boolean mSavedAlarmMuted = false; + private final Function<Integer, Boolean> mIsStreamMutedCb; private final int mMaxAlarmVolume; private int mPrivilegedAlarmActiveCount = 0; private final Consumer<AudioDeviceAttributes> mMuteAwaitConnectionTimeoutCb; private final FadeOutManager mFadeOutManager = new FadeOutManager(); PlaybackActivityMonitor(Context context, int maxAlarmVolume, - Consumer<AudioDeviceAttributes> muteTimeoutCallback) { + Consumer<AudioDeviceAttributes> muteTimeoutCallback, + Function<Integer, Boolean> isStreamMutedCb) { mContext = context; mMaxAlarmVolume = maxAlarmVolume; PlayMonitorClient.sListenerDeathMonitor = this; AudioPlaybackConfiguration.sPlayerDeathMonitor = this; mMuteAwaitConnectionTimeoutCb = muteTimeoutCallback; + mIsStreamMutedCb = isStreamMutedCb; initEventHandler(); } @@ -332,8 +337,9 @@ public final class PlaybackActivityMonitor if (mPrivilegedAlarmActiveCount++ == 0) { mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex( AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER); + mSavedAlarmMuted = mIsStreamMutedCb.apply(AudioSystem.STREAM_ALARM); AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM, - mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + mMaxAlarmVolume, /*muted=*/false, AudioSystem.DEVICE_OUT_SPEAKER); } } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { @@ -342,7 +348,8 @@ public final class PlaybackActivityMonitor AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) == mMaxAlarmVolume) { AudioSystem.setStreamVolumeIndexAS(AudioSystem.STREAM_ALARM, - mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + mSavedAlarmVolume, mSavedAlarmMuted, + AudioSystem.DEVICE_OUT_SPEAKER); } } } diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java index 1ea72d7da2fc..f66c7e115fc0 100644 --- a/services/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java @@ -36,6 +36,7 @@ import android.os.UserManager; import android.util.Slog; import com.android.server.backup.Flags; +import com.android.server.notification.NotificationBackupHelper; import com.google.android.collect.Sets; diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index acf4db30ba93..0807c70d9922 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -292,6 +292,13 @@ final class DisplayDeviceInfo { */ public float renderFrameRate; + + /** + * If {@code true}, this Display supports adaptive refresh rates. + * @see android.view.DisplayInfo#hasArrSupport for more details. + */ + public boolean hasArrSupport; + /** * The default mode of the display. */ @@ -540,7 +547,8 @@ final class DisplayDeviceInfo { other.brightnessDefault) || !Objects.equals(roundedCorners, other.roundedCorners) || installOrientation != other.installOrientation - || !Objects.equals(displayShape, other.displayShape)) { + || !Objects.equals(displayShape, other.displayShape) + || hasArrSupport != other.hasArrSupport) { diff |= DIFF_OTHER; } return diff; @@ -558,6 +566,7 @@ final class DisplayDeviceInfo { height = other.height; modeId = other.modeId; renderFrameRate = other.renderFrameRate; + hasArrSupport = other.hasArrSupport; defaultModeId = other.defaultModeId; userPreferredModeId = other.userPreferredModeId; supportedModes = other.supportedModes; @@ -602,6 +611,7 @@ final class DisplayDeviceInfo { sb.append(width).append(" x ").append(height); sb.append(", modeId ").append(modeId); sb.append(", renderFrameRate ").append(renderFrameRate); + sb.append(", hasArrSupport ").append(hasArrSupport); sb.append(", defaultModeId ").append(defaultModeId); sb.append(", userPreferredModeId ").append(userPreferredModeId); sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index c7a70fafbf26..3603cdbd775f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2301,6 +2301,11 @@ public final class DisplayManagerService extends SystemService { updateLogicalDisplayState(display); mExternalDisplayPolicy.handleLogicalDisplayAddedLocked(display); + + if (mFlags.isApplyDisplayChangedDuringDisplayAddedEnabled()) { + applyDisplayChangedLocked(display); + } + if (mDisplayTopologyCoordinator != null) { mDisplayTopologyCoordinator.onDisplayAdded(display.getDisplayInfoLocked()); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 5edea0a8b031..f9c3a46828b9 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -246,6 +246,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mActiveModeId = INVALID_MODE_ID; private boolean mDisplayModeSpecsInvalid; private int mActiveColorMode; + private boolean mHasArrSupport; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; private boolean mGameContentTypeSupported; @@ -311,6 +312,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities); changed |= updateAllmSupport(dynamicInfo.autoLowLatencyModeSupported); changed |= updateGameContentTypeSupport(dynamicInfo.gameContentTypeSupported); + changed |= updateHasArrSupportLocked(dynamicInfo.hasArrSupport); if (changed) { mHavePendingChanges = true; @@ -602,6 +604,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private boolean updateHasArrSupportLocked(boolean newHasArrSupport) { + if (mHasArrSupport == newHasArrSupport) { + return false; + } + mHasArrSupport = newHasArrSupport; + return true; + } + private boolean updateAllmSupport(boolean supported) { if (mAllmSupported == supported) { return false; @@ -684,6 +694,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.supportedColorModes[i] = mSupportedColorModes.get(i); } mInfo.hdrCapabilities = mHdrCapabilities; + mInfo.hasArrSupport = mHasArrSupport; mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos; mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos; mInfo.state = mState; @@ -1274,6 +1285,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mActiveColorMode=" + mActiveColorMode); pw.println("mDefaultModeId=" + mDefaultModeId); pw.println("mUserPreferredModeId=" + mUserPreferredModeId); + pw.println("mHasArrSupport=" + mHasArrSupport); pw.println("mState=" + Display.stateToString(mState)); pw.println("mCommittedState=" + Display.stateToString(mCommittedState)); pw.println("mBrightnessState=" + mBrightnessState); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 007e3a8fde2f..074a4d851aef 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -506,12 +506,13 @@ final class LogicalDisplay { mBaseDisplayInfo.rotation = Surface.ROTATION_0; mBaseDisplayInfo.modeId = deviceInfo.modeId; mBaseDisplayInfo.renderFrameRate = deviceInfo.renderFrameRate; + mBaseDisplayInfo.hasArrSupport = deviceInfo.hasArrSupport; mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId; mBaseDisplayInfo.userPreferredModeId = deviceInfo.userPreferredModeId; mBaseDisplayInfo.supportedModes = Arrays.copyOf( deviceInfo.supportedModes, deviceInfo.supportedModes.length); mBaseDisplayInfo.appsSupportedModes = syntheticModeManager.createAppSupportedModes( - config, mBaseDisplayInfo.supportedModes + config, mBaseDisplayInfo.supportedModes, mBaseDisplayInfo.hasArrSupport ); mBaseDisplayInfo.colorMode = deviceInfo.colorMode; mBaseDisplayInfo.supportedColorModes = Arrays.copyOf( diff --git a/services/core/java/com/android/server/display/brightness/BrightnessReason.java b/services/core/java/com/android/server/display/brightness/BrightnessReason.java index 9a0ee034a8f2..a4804e1887fe 100644 --- a/services/core/java/com/android/server/display/brightness/BrightnessReason.java +++ b/services/core/java/com/android/server/display/brightness/BrightnessReason.java @@ -50,8 +50,10 @@ public final class BrightnessReason { public static final int MODIFIER_THROTTLED = 0x8; public static final int MODIFIER_MIN_LUX = 0x10; public static final int MODIFIER_MIN_USER_SET_LOWER_BOUND = 0x20; + public static final int MODIFIER_STYLUS_UNDER_USE = 0x40; public static final int MODIFIER_MASK = MODIFIER_DIMMED | MODIFIER_LOW_POWER | MODIFIER_HDR - | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND; + | MODIFIER_THROTTLED | MODIFIER_MIN_LUX | MODIFIER_MIN_USER_SET_LOWER_BOUND + | MODIFIER_STYLUS_UNDER_USE; // ADJUSTMENT_* // These things can happen at any point, even if the main brightness reason doesn't @@ -158,6 +160,9 @@ public final class BrightnessReason { if ((mModifier & MODIFIER_MIN_USER_SET_LOWER_BOUND) != 0) { sb.append(" user_min_pref"); } + if ((mModifier & MODIFIER_STYLUS_UNDER_USE) != 0) { + sb.append(" stylus_under_use"); + } int strlen = sb.length(); if (sb.charAt(strlen - 1) == '[') { sb.setLength(strlen - 2); diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index 71fdaf3f85b6..4bd980822e46 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -110,6 +110,9 @@ public final class DisplayBrightnessController { @VisibleForTesting AutomaticBrightnessController mAutomaticBrightnessController; + // True if the stylus is being used + private boolean mIsStylusBeingUsed; + /** * The constructor of DisplayBrightnessController. */ @@ -460,6 +463,8 @@ public final class DisplayBrightnessController { writer.println(" mScreenBrightnessDefault=" + mScreenBrightnessDefault); writer.println(" mPersistBrightnessNitsForDefaultDisplay=" + mPersistBrightnessNitsForDefaultDisplay); + writer.println(" mIsStylusBeingUsed=" + + mIsStylusBeingUsed); synchronized (mLock) { writer.println(" mPendingScreenBrightness=" + mPendingScreenBrightness); writer.println(" mCurrentScreenBrightness=" + mCurrentScreenBrightness); @@ -505,7 +510,12 @@ public final class DisplayBrightnessController { * Notifies if the stylus is currently being used or not. */ public void setStylusBeingUsed(boolean isEnabled) { - // Todo(b/369977976) - Disable the auto-brightness strategy + mIsStylusBeingUsed = isEnabled; + } + + @VisibleForTesting + boolean isStylusBeingUsed() { + return mIsStylusBeingUsed; } @VisibleForTesting @@ -626,13 +636,14 @@ public final class DisplayBrightnessController { lastUserSetScreenBrightness = mLastUserSetScreenBrightness; } return new StrategySelectionRequest(displayPowerRequest, targetDisplayState, - lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession); + lastUserSetScreenBrightness, userSetBrightnessChanged, displayOffloadSession, + mIsStylusBeingUsed); } private StrategyExecutionRequest constructStrategyExecutionRequest( DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) { float currentScreenBrightness = getCurrentBrightness(); return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness, - mUserSetScreenBrightnessUpdated); + mUserSetScreenBrightnessUpdated, mIsStylusBeingUsed); } } diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java index 06890e72f068..ded7447c5fbc 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java @@ -306,7 +306,8 @@ public class DisplayBrightnessStrategySelector { strategySelectionRequest.getDisplayPowerRequest().useNormalBrightnessForDoze, strategySelectionRequest.getLastUserSetScreenBrightness(), strategySelectionRequest.isUserSetBrightnessChanged()); - return mAutomaticBrightnessStrategy1.isAutoBrightnessValid(); + return !strategySelectionRequest.isStylusBeingUsed() + && mAutomaticBrightnessStrategy1.isAutoBrightnessValid(); } private StrategySelectionNotifyRequest constructStrategySelectionNotifyRequest( diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java index 304640b884ef..7a07c4fc22bf 100644 --- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java @@ -32,11 +32,15 @@ public final class StrategyExecutionRequest { // Represents if the user set screen brightness was changed or not. private boolean mUserSetBrightnessChanged; + private boolean mIsStylusBeingUsed; + public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, - float currentScreenBrightness, boolean userSetBrightnessChanged) { + float currentScreenBrightness, boolean userSetBrightnessChanged, + boolean isStylusBeingUsed) { mDisplayPowerRequest = displayPowerRequest; mCurrentScreenBrightness = currentScreenBrightness; mUserSetBrightnessChanged = userSetBrightnessChanged; + mIsStylusBeingUsed = isStylusBeingUsed; } public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { @@ -51,6 +55,10 @@ public final class StrategyExecutionRequest { return mUserSetBrightnessChanged; } + public boolean isStylusBeingUsed() { + return mIsStylusBeingUsed; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof StrategyExecutionRequest)) { @@ -59,12 +67,13 @@ public final class StrategyExecutionRequest { StrategyExecutionRequest other = (StrategyExecutionRequest) obj; return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest()) && mCurrentScreenBrightness == other.getCurrentScreenBrightness() - && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged(); + && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged() + && mIsStylusBeingUsed == other.isStylusBeingUsed(); } @Override public int hashCode() { return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness, - mUserSetBrightnessChanged); + mUserSetBrightnessChanged, mIsStylusBeingUsed); } } diff --git a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java index aa2f23ef9ec1..5c1f03d877a6 100644 --- a/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java +++ b/services/core/java/com/android/server/display/brightness/StrategySelectionRequest.java @@ -40,15 +40,19 @@ public final class StrategySelectionRequest { private DisplayManagerInternal.DisplayOffloadSession mDisplayOffloadSession; + private boolean mIsStylusBeingUsed; + public StrategySelectionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest, int targetDisplayState, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged, - DisplayManagerInternal.DisplayOffloadSession displayOffloadSession) { + DisplayManagerInternal.DisplayOffloadSession displayOffloadSession, + boolean isStylusBeingUsed) { mDisplayPowerRequest = displayPowerRequest; mTargetDisplayState = targetDisplayState; mLastUserSetScreenBrightness = lastUserSetScreenBrightness; mUserSetBrightnessChanged = userSetBrightnessChanged; mDisplayOffloadSession = displayOffloadSession; + mIsStylusBeingUsed = isStylusBeingUsed; } public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() { @@ -72,6 +76,10 @@ public final class StrategySelectionRequest { return mDisplayOffloadSession; } + public boolean isStylusBeingUsed() { + return mIsStylusBeingUsed; + } + @Override public boolean equals(Object obj) { if (!(obj instanceof StrategySelectionRequest)) { @@ -82,12 +90,14 @@ public final class StrategySelectionRequest { && mTargetDisplayState == other.getTargetDisplayState() && mLastUserSetScreenBrightness == other.getLastUserSetScreenBrightness() && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged() - && mDisplayOffloadSession.equals(other.getDisplayOffloadSession()); + && mDisplayOffloadSession.equals(other.getDisplayOffloadSession()) + && mIsStylusBeingUsed == other.isStylusBeingUsed(); } @Override public int hashCode() { return Objects.hash(mDisplayPowerRequest, mTargetDisplayState, - mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession); + mLastUserSetScreenBrightness, mUserSetBrightnessChanged, mDisplayOffloadSession, + mIsStylusBeingUsed); } } diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java index 7c0c9312888e..b9de34a80bda 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java @@ -37,6 +37,9 @@ public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{ StrategyExecutionRequest strategyExecutionRequest) { BrightnessReason brightnessReason = new BrightnessReason(); brightnessReason.setReason(BrightnessReason.REASON_MANUAL); + if (strategyExecutionRequest.isStylusBeingUsed()) { + brightnessReason.setModifier(BrightnessReason.MODIFIER_STYLUS_UNDER_USE); + } return new DisplayBrightnessState.Builder() .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness()) .setBrightnessReason(brightnessReason) 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 b2e98bc05e75..07343f469ed7 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -222,11 +222,21 @@ public class DisplayManagerFlags { Flags::enableWaitingConfirmationBeforeMirroring ); + private final FlagState mEnableApplyDisplayChangedDuringDisplayAdded = new FlagState( + Flags.FLAG_ENABLE_APPLY_DISPLAY_CHANGED_DURING_DISPLAY_ADDED, + Flags::enableApplyDisplayChangedDuringDisplayAdded + ); + private final FlagState mEnableBatteryStatsForAllDisplays = new FlagState( Flags.FLAG_ENABLE_BATTERY_STATS_FOR_ALL_DISPLAYS, Flags::enableBatteryStatsForAllDisplays ); + private final FlagState mHasArrSupport = new FlagState( + Flags.FLAG_ENABLE_HAS_ARR_SUPPORT, + Flags::enableHasArrSupport + ); + /** * @return {@code true} if 'port' is allowed in display layout configuration file. */ @@ -466,6 +476,13 @@ public class DisplayManagerFlags { } /** + * @return {@code true} if need to apply display changes during display added event. + */ + public boolean isApplyDisplayChangedDuringDisplayAddedEnabled() { + return mEnableApplyDisplayChangedDuringDisplayAdded.isEnabled(); + } + + /** * @return {@code true} if autobrightness is to be blocked when stylus is being used */ public boolean isBlockAutobrightnessChangesOnStylusUsage() { @@ -481,6 +498,12 @@ public class DisplayManagerFlags { } /** + * @return {@code true} if hasArrSupport API is enabled. + */ + public boolean hasArrSupportFlag() { + return mHasArrSupport.isEnabled(); + } + /** * dumps all flagstates * @param pw printWriter */ @@ -526,8 +549,10 @@ public class DisplayManagerFlags { pw.println(" " + mIdleScreenConfigInSubscribingLightSensor); pw.println(" " + mEnableWaitingConfirmationBeforeMirroring); pw.println(" " + mEnableBatteryStatsForAllDisplays); + pw.println(" " + mEnableApplyDisplayChangedDuringDisplayAdded); pw.println(" " + mBlockAutobrightnessChangesOnStylusUsage); pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled); + pw.println(" " + mHasArrSupport); } private static class FlagState { diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index df626385c5cc..ddb29691f42e 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -386,6 +386,17 @@ flag { } flag { + name: "enable_apply_display_changed_during_display_added" + namespace: "display_manager" + description: "Apply display changes after display added" + bug: "368131655" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "block_autobrightness_changes_on_stylus_usage" namespace: "display_manager" description: "Block the usage of ALS to control the display brightness when stylus is being used" @@ -403,3 +414,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_has_arr_support" + namespace: "core_graphics" + description: "Flag for an API to get whether display supports ARR or not" + bug: "361433651" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index ffa64bfcf29f..88562ab9ba2d 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -156,6 +156,8 @@ public class DisplayModeDirector { // a map from display id to display device config private SparseArray<DisplayDeviceConfig> mDisplayDeviceConfigByDisplay = new SparseArray<>(); + private SparseBooleanArray mHasArrSupport; + private BrightnessObserver mBrightnessObserver; private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; @@ -194,6 +196,8 @@ public class DisplayModeDirector { private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled; + private final boolean mHasArrSupportFlagEnabled; + private final DisplayManagerFlags mDisplayManagerFlags; private final DisplayDeviceConfigProvider mDisplayDeviceConfigProvider; @@ -218,6 +222,7 @@ public class DisplayModeDirector { .isDisplaysRefreshRatesSynchronizationEnabled(); mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled(); + mHasArrSupportFlagEnabled = displayManagerFlags.hasArrSupportFlag(); mDisplayManagerFlags = displayManagerFlags; mDisplayDeviceConfigProvider = displayDeviceConfigProvider; mContext = context; @@ -228,6 +233,7 @@ public class DisplayModeDirector { mSupportedModesByDisplay = new SparseArray<>(); mAppSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); + mHasArrSupport = new SparseBooleanArray(); mAppRequestObserver = new AppRequestObserver(displayManagerFlags); mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig()); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); @@ -452,7 +458,13 @@ public class DisplayModeDirector { return mAppRequestObserver; } + // TODO(b/372019752) Rename all the occurrences of the VRR with ARR. private boolean isVrrSupportedLocked(int displayId) { + if (mHasArrSupportFlagEnabled) { + Boolean hasArrSupport = mHasArrSupport.get(displayId); + return hasArrSupport != null && hasArrSupport; + } + // TODO(b/371041638) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.get(displayId); return config != null && config.isVrrSupportEnabled(); } @@ -1469,6 +1481,7 @@ public class DisplayModeDirector { DisplayInfo displayInfo = getDisplayInfo(displayId); registerExternalDisplay(displayInfo); updateDisplayModes(displayId, displayInfo); + updateHasArrSupport(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); updateUserSettingDisplayPreferredSize(displayInfo); updateDisplaysPeakRefreshRateAndResolution(displayInfo); @@ -1482,6 +1495,7 @@ public class DisplayModeDirector { mDefaultModeByDisplay.remove(displayId); mDisplayDeviceConfigByDisplay.remove(displayId); mSettingsObserver.removeRefreshRateSetting(displayId); + mHasArrSupport.delete(displayId); } updateLayoutLimitedFrameRate(displayId, null); removeUserSettingDisplayPreferredSize(displayId); @@ -1493,6 +1507,7 @@ public class DisplayModeDirector { public void onDisplayChanged(int displayId) { updateDisplayDeviceConfig(displayId); DisplayInfo displayInfo = getDisplayInfo(displayId); + updateHasArrSupport(displayId, displayInfo); updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); updateUserSettingDisplayPreferredSize(displayInfo); @@ -1691,6 +1706,16 @@ public class DisplayModeDirector { } } } + + private void updateHasArrSupport(int displayId, @Nullable DisplayInfo info) { + if (info == null) { + return; + } + synchronized (mLock) { + mHasArrSupport.put(displayId, info.hasArrSupport); + } + } + } /** diff --git a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java index a83b9395dfc0..71b34679882d 100644 --- a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java +++ b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java @@ -37,17 +37,22 @@ public class SyntheticModeManager { SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE; private final boolean mSynthetic60HzModesEnabled; + private final boolean mHasArrSupportEnabled; public SyntheticModeManager(DisplayManagerFlags flags) { mSynthetic60HzModesEnabled = flags.isSynthetic60HzModesEnabled(); + mHasArrSupportEnabled = flags.hasArrSupportFlag(); } /** * creates display supportedModes array, exposed to applications */ public Display.Mode[] createAppSupportedModes(DisplayDeviceConfig config, - Display.Mode[] modes) { - if (!config.isVrrSupportEnabled() || !mSynthetic60HzModesEnabled) { + Display.Mode[] modes, boolean hasArrSupport) { + // TODO(b/361433651) Remove config.isVrrSupportEnabled once hasArrSupport is rolled out + boolean isArrSupported = + mHasArrSupportEnabled ? hasArrSupport : config.isVrrSupportEnabled(); + if (!isArrSupported || !mSynthetic60HzModesEnabled) { return modes; } List<Display.Mode> appSupportedModes = new ArrayList<>(); diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 5514ec701c15..636854b85ee4 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -62,9 +62,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.parsing.PackageParser2; import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FrameworkStatsLog; import com.android.server.LocalServices; -import com.android.server.integrity.engine.RuleEvaluationEngine; import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.integrity.model.RuleMetadata; import com.android.server.pm.PackageManagerServiceUtils; @@ -131,7 +129,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private final Handler mHandler; private final PackageManagerInternal mPackageManagerInternal; private final Supplier<PackageParser2> mParserSupplier; - private final RuleEvaluationEngine mEvaluationEngine; private final IntegrityFileManager mIntegrityFileManager; /** Create an instance of {@link AppIntegrityManagerServiceImpl}. */ @@ -143,7 +140,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { context, LocalServices.getService(PackageManagerInternal.class), PackageParserUtils::forParsingFileWithDefaults, - RuleEvaluationEngine.getRuleEvaluationEngine(), IntegrityFileManager.getInstance(), handlerThread.getThreadHandler()); } @@ -153,13 +149,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { Context context, PackageManagerInternal packageManagerInternal, Supplier<PackageParser2> parserSupplier, - RuleEvaluationEngine evaluationEngine, IntegrityFileManager integrityFileManager, Handler handler) { mContext = context; mPackageManagerInternal = packageManagerInternal; mParserSupplier = parserSupplier; - mEvaluationEngine = evaluationEngine; mIntegrityFileManager = integrityFileManager; mHandler = handler; @@ -214,12 +208,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { version, ruleProvider)); } - FrameworkStatsLog.write( - FrameworkStatsLog.INTEGRITY_RULES_PUSHED, - success, - ruleProvider, - version); - Intent intent = new Intent(); intent.putExtra(EXTRA_STATUS, success ? STATUS_SUCCESS : STATUS_FAILURE); try { @@ -337,7 +325,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { + " installers " + allowedInstallers); } - IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); + IntegrityCheckResult result = IntegrityCheckResult.allow(); if (!result.getMatchedRules().isEmpty() || DEBUG_INTEGRITY_COMPONENT) { Slog.i( TAG, @@ -346,15 +334,6 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { packageName, result.getEffect(), result.getMatchedRules())); } - FrameworkStatsLog.write( - FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED, - packageName, - appCertificates.toString(), - appInstallMetadata.getVersionCode(), - installerPackageName, - result.getLoggingResponse(), - result.isCausedByAppCertRule(), - result.isCausedByInstallerRule()); mPackageManagerInternal.setIntegrityVerificationResult( verificationId, result.getEffect() == IntegrityCheckResult.Effect.ALLOW diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java deleted file mode 100644 index 61da45ddbfef..000000000000 --- a/services/core/java/com/android/server/integrity/engine/RuleEvaluationEngine.java +++ /dev/null @@ -1,85 +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.server.integrity.engine; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.Rule; -import android.util.Slog; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.integrity.IntegrityFileManager; -import com.android.server.integrity.model.IntegrityCheckResult; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * The engine used to evaluate rules against app installs. - * - * <p>Every app install is evaluated against rules (pushed by the verifier) by the evaluation engine - * to allow/block that install. - */ -public class RuleEvaluationEngine { - private static final String TAG = "RuleEvaluation"; - - // The engine for loading rules, retrieving metadata for app installs, and evaluating app - // installs against rules. - private static RuleEvaluationEngine sRuleEvaluationEngine; - - private final IntegrityFileManager mIntegrityFileManager; - - @VisibleForTesting - RuleEvaluationEngine(IntegrityFileManager integrityFileManager) { - mIntegrityFileManager = integrityFileManager; - } - - /** Provide a singleton instance of the rule evaluation engine. */ - public static synchronized RuleEvaluationEngine getRuleEvaluationEngine() { - if (sRuleEvaluationEngine == null) { - return new RuleEvaluationEngine(IntegrityFileManager.getInstance()); - } - return sRuleEvaluationEngine; - } - - /** - * Load, and match the list of rules against an app install metadata. - * - * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules - * against. - * @return result of the integrity check - */ - public IntegrityCheckResult evaluate( - AppInstallMetadata appInstallMetadata) { - List<Rule> rules = loadRules(appInstallMetadata); - return RuleEvaluator.evaluateRules(rules, appInstallMetadata); - } - - private List<Rule> loadRules(AppInstallMetadata appInstallMetadata) { - if (!mIntegrityFileManager.initialized()) { - Slog.w(TAG, "Integrity rule files are not available."); - return Collections.emptyList(); - } - - try { - return mIntegrityFileManager.readRules(appInstallMetadata); - } catch (Exception e) { - Slog.e(TAG, "Error loading rules.", e); - return new ArrayList<>(); - } - } -} diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java deleted file mode 100644 index 9d9430441e07..000000000000 --- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java +++ /dev/null @@ -1,81 +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.server.integrity.engine; - -import static android.content.integrity.Rule.DENY; -import static android.content.integrity.Rule.FORCE_ALLOW; - -import android.annotation.NonNull; -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.Rule; - -import com.android.server.integrity.model.IntegrityCheckResult; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * A helper class for evaluating rules against app install metadata to find if there are matching - * rules. - */ -final class RuleEvaluator { - - /** - * Match the list of rules against an app install metadata. - * - * <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas - * only. All rules are OR'ed together by default. - * - * @param rules The list of rules to evaluate. - * @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules - * against. - * @return result of the integrity check - */ - @NonNull - static IntegrityCheckResult evaluateRules( - List<Rule> rules, AppInstallMetadata appInstallMetadata) { - - // Identify the rules that match the {@code appInstallMetadata}. - List<Rule> matchedRules = - rules.stream() - .filter(rule -> rule.getFormula().matches(appInstallMetadata)) - .collect(Collectors.toList()); - - // Identify the matched power allow rules and terminate early if we have any. - List<Rule> matchedPowerAllowRules = - matchedRules.stream() - .filter(rule -> rule.getEffect() == FORCE_ALLOW) - .collect(Collectors.toList()); - - if (!matchedPowerAllowRules.isEmpty()) { - return IntegrityCheckResult.allow(matchedPowerAllowRules); - } - - // Identify the matched deny rules. - List<Rule> matchedDenyRules = - matchedRules.stream() - .filter(rule -> rule.getEffect() == DENY) - .collect(Collectors.toList()); - - if (!matchedDenyRules.isEmpty()) { - return IntegrityCheckResult.deny(matchedDenyRules); - } - - // When no rules are denied, return default allow result. - return IntegrityCheckResult.allow(); - } -} diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java index 1fa067065e1b..b0647fc46473 100644 --- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java +++ b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java @@ -19,8 +19,6 @@ package com.android.server.integrity.model; import android.annotation.Nullable; import android.content.integrity.Rule; -import com.android.internal.util.FrameworkStatsLog; - import java.util.Collections; import java.util.List; @@ -82,21 +80,6 @@ public final class IntegrityCheckResult { return new IntegrityCheckResult(Effect.DENY, ruleList); } - /** - * Returns the in value of the integrity check result for logging purposes. - */ - public int getLoggingResponse() { - if (getEffect() == Effect.DENY) { - return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED; - } else if (getEffect() == Effect.ALLOW && getMatchedRules().isEmpty()) { - return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED; - } else if (getEffect() == Effect.ALLOW && !getMatchedRules().isEmpty()) { - return FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED; - } else { - throw new IllegalStateException("IntegrityCheckResult is not valid."); - } - } - /** Returns true when the {@code mEffect} is caused by an app certificate mismatch. */ public boolean isCausedByAppCertRule() { return mRuleList.stream().anyMatch(rule -> rule.getFormula().isAppCertificateFormula()); diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index 741513cf3c0b..7e80cbcb583e 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -221,7 +221,7 @@ public class LocaleManagerService extends SystemService { public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { - (new LocaleManagerShellCommand(mBinderService)) + (new LocaleManagerShellCommand(mBinderService, mContext)) .exec(this, in, out, err, args, callback, resultReceiver); } diff --git a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java index 09f2ffa35da5..926b64744a76 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java +++ b/services/core/java/com/android/server/locales/LocaleManagerShellCommand.java @@ -19,6 +19,8 @@ package com.android.server.locales; import android.app.ActivityManager; import android.app.ILocaleManager; import android.app.LocaleConfig; +import android.content.Context; +import android.content.pm.PackageManager; import android.os.LocaleList; import android.os.RemoteException; import android.os.ShellCommand; @@ -31,9 +33,11 @@ import java.io.PrintWriter; */ public class LocaleManagerShellCommand extends ShellCommand { private final ILocaleManager mBinderService; + private final Context mContext; - LocaleManagerShellCommand(ILocaleManager localeManager) { + LocaleManagerShellCommand(ILocaleManager localeManager, Context context) { mBinderService = localeManager; + mContext = context; } @Override public int onCommand(String cmd) { @@ -49,6 +53,8 @@ public class LocaleManagerShellCommand extends ShellCommand { return runSetAppOverrideLocaleConfig(); case "get-app-localeconfig": return runGetAppOverrideLocaleConfig(); + case "get-app-localeconfig-ignore-override": + return runGetAppLocaleConfigIgnoreOverride(); default: { return handleDefaultCommands(cmd); } @@ -261,6 +267,55 @@ public class LocaleManagerShellCommand extends ShellCommand { return 0; } + private int runGetAppLocaleConfigIgnoreOverride() { + String packageName = getNextArg(); + final PrintWriter err = getErrPrintWriter(); + + if (packageName != null) { + int userId = ActivityManager.getCurrentUser(); + do { + String option = getNextOption(); + if (option == null) { + break; + } + if ("--user".equals(option)) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + } else { + throw new IllegalArgumentException("Unknown option: " + option); + } + } while (true); + LocaleConfig resLocaleConfig = null; + try { + resLocaleConfig = LocaleConfig.fromContextIgnoringOverride( + mContext.createPackageContextAsUser(packageName, /* flags= */ 0, + UserHandle.of(userId))); + } catch (PackageManager.NameNotFoundException e) { + err.println("Unknown package name " + packageName + " for user " + userId); + return -1; + } + if (resLocaleConfig == null) { + getOutPrintWriter().println( + "LocaleConfig for " + packageName + " for user " + userId + " is null"); + } else { + LocaleList locales = resLocaleConfig.getSupportedLocales(); + if (locales == null) { + getOutPrintWriter().println( + "Locales within the LocaleConfig for " + packageName + " for user " + + userId + " are null"); + } else { + getOutPrintWriter().println( + "Locales within the LocaleConfig for " + packageName + " for user " + + userId + " are [" + locales.toLanguageTags() + "]"); + } + } + } else { + err.println("Error: no package specified"); + return -1; + } + return 0; + } + private LocaleList parseOverrideLocales() { String locales = getNextArg(); if (locales == null) { diff --git a/services/core/java/com/android/server/media/AudioManagerRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java index c79f41d32b29..25e1c94aa689 100644 --- a/services/core/java/com/android/server/media/AudioManagerRouteController.java +++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java @@ -420,7 +420,7 @@ import java.util.Objects; // to derive a name ourselves from the type instead. String deviceName = audioDeviceInfo.getPort().name(); - if (!TextUtils.isEmpty(address)) { + if (mBluetoothRouteController.containsBondedDeviceWithAddress(address)) { routeId = mBluetoothRouteController.getRouteIdForBluetoothAddress(address); deviceName = mBluetoothRouteController.getNameForBluetoothAddress(address); } diff --git a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java index 8b65ea305ad8..c79d6e5400cd 100644 --- a/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java +++ b/services/core/java/com/android/server/media/BluetoothDeviceRoutesManager.java @@ -141,6 +141,11 @@ import java.util.stream.Collectors; mContext.unregisterReceiver(mDeviceStateChangedReceiver); } + /** Returns true if the given address corresponds to a currently-bonded Bluetooth device. */ + public synchronized boolean containsBondedDeviceWithAddress(@Nullable String address) { + return mAddressToBondedDevice.containsKey(address); + } + @Nullable public synchronized String getRouteIdForBluetoothAddress(@Nullable String address) { BluetoothDevice bluetoothDevice = mAddressToBondedDevice.get(address); diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java index cfcf6ebf9540..f186f4273f61 100644 --- a/services/core/java/com/android/server/notification/CalendarTracker.java +++ b/services/core/java/com/android/server/notification/CalendarTracker.java @@ -32,6 +32,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.util.Date; import java.util.Objects; @@ -296,4 +298,8 @@ public class CalendarTracker { void onChanged(); } + @VisibleForTesting // (otherwise = NONE) + public int getUserId() { + return mUserContext.getUserId(); + } } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 66e61c076030..0b40d64e3a09 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -169,16 +169,15 @@ public class ConditionProviders extends ManagedServices { for (int i = 0; i < mSystemConditionProviders.size(); i++) { mSystemConditionProviders.valueAt(i).onBootComplete(); } - if (mCallback != null) { - mCallback.onBootComplete(); - } } @Override public void onUserSwitched(int user) { super.onUserSwitched(user); - if (mCallback != null) { - mCallback.onUserSwitched(); + if (android.app.Flags.modesHsum()) { + for (int i = 0; i < mSystemConditionProviders.size(); i++) { + mSystemConditionProviders.valueAt(i).onUserSwitched(UserHandle.of(user)); + } } } @@ -515,10 +514,8 @@ public class ConditionProviders extends ManagedServices { } public interface Callback { - void onBootComplete(); void onServiceAdded(ComponentName component); void onConditionChanged(Uri id, Condition condition); - void onUserSwitched(); } } diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java index c3ace15a8c0f..bb8ccd20f9ab 100644 --- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java +++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; +import android.os.UserHandle; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; import android.text.format.DateUtils; @@ -65,6 +66,11 @@ public class CountdownConditionProvider extends SystemConditionProviderService { } @Override + public void onUserSwitched(UserHandle user) { + // Nothing to do because countdown conditions are not tied to any user data. + } + + @Override public void dump(PrintWriter pw, DumpFilter filter) { pw.println(" CountdownConditionProvider:"); pw.print(" mConnected="); pw.println(mConnected); diff --git a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java index 9531c5e58851..52dc116c699d 100644 --- a/services/core/java/com/android/server/notification/CustomManualConditionProvider.java +++ b/services/core/java/com/android/server/notification/CustomManualConditionProvider.java @@ -17,6 +17,7 @@ package com.android.server.notification; import android.net.Uri; +import android.os.UserHandle; import android.service.notification.ZenModeConfig; import java.io.PrintWriter; @@ -40,6 +41,11 @@ public class CustomManualConditionProvider extends SystemConditionProviderServic } @Override + public void onUserSwitched(UserHandle user) { + // Nothing to do because we won't ever call notifyConditions. + } + + @Override public void onConnected() { // No need to keep subscriptions because we won't ever call notifyConditions } diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java index 00dd547f6c4e..ecc4cf72a1c3 100644 --- a/services/core/java/com/android/server/notification/EventConditionProvider.java +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import android.annotation.Nullable; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -23,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; @@ -37,6 +39,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.notification.CalendarTracker.CheckEventResult; import com.android.server.notification.NotificationManagerService.DumpFilter; import com.android.server.pm.PackageManagerService; @@ -59,12 +62,13 @@ public class EventConditionProvider extends SystemConditionProviderService { private static final String EXTRA_TIME = "time"; private static final long CHANGE_DELAY = 2 * 1000; // coalesce chatty calendar changes - private final Context mContext = this; + @VisibleForTesting Context mContext = this; private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>(); private final SparseArray<CalendarTracker> mTrackers = new SparseArray<>(); private final Handler mWorker; private final HandlerThread mThread; + @Nullable private UserHandle mCurrentUser; private boolean mConnected; private boolean mRegistered; private boolean mBootComplete; // don't hammer the calendar provider until boot completes. @@ -114,10 +118,28 @@ public class EventConditionProvider extends SystemConditionProviderService { mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - reloadTrackers(); + if (android.app.Flags.modesHsum()) { + if (mCurrentUser != null) { + // Possibly the intent signals a profile added on a different user, but it + // doesn't matter (except for a bit of wasted work here). We will reload + // trackers for that user when we switch. + reloadTrackers(mCurrentUser); + } + } else { + reloadTrackers(); + } } }, filter); - reloadTrackers(); + if (!android.app.Flags.modesHsum()) { + reloadTrackers(); + } + } + + @Override + public void onUserSwitched(UserHandle user) { + if (DEBUG) Slog.d(TAG, "onUserSwitched: " + user); + mCurrentUser = user; + reloadTrackers(user); } @Override @@ -157,8 +179,41 @@ public class EventConditionProvider extends SystemConditionProviderService { } } + private void reloadTrackers(UserHandle user) { + if (DEBUG) Slog.d(TAG, "reloadTrackers user=" + user); + for (int i = 0; i < mTrackers.size(); i++) { + mTrackers.valueAt(i).setCallback(null); + } + mTrackers.clear(); + + // Ensure that user is the main user and not a profile. + UserManager userManager = UserManager.get(mContext); + UserHandle possibleParent = userManager.getProfileParent(user); + if (possibleParent != null) { + Slog.wtf(TAG, "reloadTrackers should not be called with profile " + user + + "; continuing with parent " + possibleParent); + user = possibleParent; + } + + for (UserInfo profile : userManager.getProfiles(user.getIdentifier())) { + final Context profileContext = getContextForUser(mContext, profile.getUserHandle()); + if (profileContext == null) { + Slog.w(TAG, "Unable to create context for profile " + profile.id + " of user " + + user.getIdentifier()); + continue; + } + mTrackers.put(profile.id, new CalendarTracker(mContext, profileContext)); + } + evaluateSubscriptions(); + } + + @Deprecated // Remove when inlining MODES_HSUM private void reloadTrackers() { if (DEBUG) Slog.d(TAG, "reloadTrackers"); + if (android.app.Flags.modesHsum()) { + Slog.wtf(TAG, "Shouldn't call reloadTrackers() without user in MODES_HSUM", + new Exception()); + } for (int i = 0; i < mTrackers.size(); i++) { mTrackers.valueAt(i).setCallback(null); } @@ -325,4 +380,9 @@ public class EventConditionProvider extends SystemConditionProviderService { evaluateSubscriptionsW(); } }; + + @VisibleForTesting // (otherwise = NONE) + public SparseArray<CalendarTracker> getTrackers() { + return mTrackers; + } } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 03fc60cad8d6..93482e769a2b 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -712,13 +712,21 @@ abstract public class ManagedServices { } } readExtraAttributes(tag, parser, resolvedUserId); - if (allowedManagedServicePackages == null || allowedManagedServicePackages.test( - getPackageName(approved), resolvedUserId, getRequiredPermission()) - || approved.isEmpty()) { - if (mUm.getUserInfo(resolvedUserId) != null) { - addApprovedList(approved, resolvedUserId, isPrimary, userSetComponent); - } + if (isUserChanged != null && approved.isEmpty()) { + // NAS + denyPregrantedAppUserSet(resolvedUserId, isPrimary); mUseXml = true; + } else { + if (allowedManagedServicePackages == null + || allowedManagedServicePackages.test( + getPackageName(approved), resolvedUserId, getRequiredPermission()) + || approved.isEmpty()) { + if (mUm.getUserInfo(resolvedUserId) != null) { + addApprovedList(approved, resolvedUserId, isPrimary, + userSetComponent); + } + mUseXml = true; + } } } else { readExtraTag(tag, parser); @@ -826,6 +834,17 @@ abstract public class ManagedServices { } } + protected void denyPregrantedAppUserSet(int userId, boolean isPrimary) { + synchronized (mApproved) { + ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); + if (approvedByType == null) { + approvedByType = new ArrayMap<>(); + mApproved.put(userId, approvedByType); + } + approvedByType.put(isPrimary, new ArraySet<>()); + } + } + protected boolean isComponentEnabledForPackage(String pkg) { synchronized (mMutex) { return mEnabledServicesPackageNames.contains(pkg); diff --git a/core/java/com/android/server/backup/NotificationBackupHelper.java b/services/core/java/com/android/server/notification/NotificationBackupHelper.java index faa0509086fc..ee9ec159a5e0 100644 --- a/core/java/com/android/server/backup/NotificationBackupHelper.java +++ b/services/core/java/com/android/server/notification/NotificationBackupHelper.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.backup; +package com.android.server.notification; import android.app.INotificationManager; import android.app.backup.BlobBackupHelper; @@ -22,6 +22,8 @@ import android.os.ServiceManager; import android.util.Log; import android.util.Slog; +import com.android.server.LocalServices; + public class NotificationBackupHelper extends BlobBackupHelper { static final String TAG = "NotifBackupHelper"; // must be < 23 chars static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -34,9 +36,13 @@ public class NotificationBackupHelper extends BlobBackupHelper { private final int mUserId; + private final NotificationManagerInternal mNm; + public NotificationBackupHelper(int userId) { super(BLOB_VERSION, KEY_NOTIFICATIONS); mUserId = userId; + + mNm = LocalServices.getService(NotificationManagerInternal.class); } @Override @@ -44,9 +50,13 @@ public class NotificationBackupHelper extends BlobBackupHelper { byte[] newPayload = null; if (KEY_NOTIFICATIONS.equals(key)) { try { - INotificationManager nm = INotificationManager.Stub.asInterface( - ServiceManager.getService("notification")); - newPayload = nm.getBackupPayload(mUserId); + if (android.app.Flags.backupRestoreLogging()) { + newPayload = mNm.getBackupPayload(mUserId, getLogger()); + } else { + INotificationManager nm = INotificationManager.Stub.asInterface( + ServiceManager.getService("notification")); + newPayload = nm.getBackupPayload(mUserId); + } } catch (Exception e) { // Treat as no data Slog.e(TAG, "Couldn't communicate with notification manager", e); @@ -64,9 +74,13 @@ public class NotificationBackupHelper extends BlobBackupHelper { if (KEY_NOTIFICATIONS.equals(key)) { try { - INotificationManager nm = INotificationManager.Stub.asInterface( - ServiceManager.getService("notification")); - nm.applyRestore(payload, mUserId); + if (android.app.Flags.backupRestoreLogging()) { + mNm.applyRestore(payload, mUserId, getLogger()); + } else { + INotificationManager nm = INotificationManager.Stub.asInterface( + ServiceManager.getService("notification")); + nm.applyRestore(payload, mUserId); + } } catch (Exception e) { Slog.e(TAG, "Couldn't communicate with notification manager", e); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index 4b8de4e8c6f1..d5d4070ee4c3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.notification; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.backup.BackupRestoreEventLogger; import android.service.notification.DeviceEffectsApplier; import java.util.Set; @@ -43,7 +44,7 @@ public interface NotificationManagerInternal { void onConversationRemoved(String pkg, int uid, Set<String> shortcuts); - /** Get the number of notification channels for a given package */ + /** Get the number of app created notification channels for a given package */ int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted); /** Does the specified package/uid have permission to post notifications? */ @@ -73,4 +74,9 @@ public interface NotificationManagerInternal { * Otherwise an {@link IllegalStateException} will be thrown. */ void setDeviceEffectsApplier(DeviceEffectsApplier applier); + + // Backup/restore interface + byte[] getBackupPayload(int user, BackupRestoreEventLogger logger); + + void applyRestore(byte[] payload, int user, BackupRestoreEventLogger logger); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 88334ebe2abb..62d762244617 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -216,6 +216,7 @@ import android.app.StatsManager; import android.app.UriGrantsManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupManager; +import android.app.backup.BackupRestoreEventLogger; import android.app.compat.CompatChanges; import android.app.role.OnRoleHoldersChangedListener; import android.app.role.RoleManager; @@ -462,7 +463,7 @@ public class NotificationManagerService extends SystemService { static final int INVALID_UID = -1; static final String ROOT_PKG = "root"; - static final String[] ALLOWED_ADJUSTMENTS = new String[] { + static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] { Adjustment.KEY_PEOPLE, Adjustment.KEY_SNOOZE_CRITERIA, Adjustment.KEY_USER_SENTIMENT, @@ -1102,7 +1103,8 @@ public class NotificationManagerService extends SystemService { return false; } - void readPolicyXml(InputStream stream, boolean forRestore, int userId) + void readPolicyXml(InputStream stream, boolean forRestore, int userId, + BackupRestoreEventLogger logger) throws XmlPullParserException, NumberFormatException, IOException { final TypedXmlPullParser parser; if (forRestore) { @@ -1189,7 +1191,7 @@ public class NotificationManagerService extends SystemService { InputStream infile = null; try { infile = mPolicyFile.openRead(); - readPolicyXml(infile, false /*forRestore*/, USER_ALL); + readPolicyXml(infile, false /*forRestore*/, USER_ALL, null); // We re-load the default dnd packages to allow the newly added and denined. final boolean isWatch = mPackageManagerClient.hasSystemFeature( @@ -1239,7 +1241,7 @@ public class NotificationManagerService extends SystemService { } try { - writePolicyXml(stream, false /*forBackup*/, USER_ALL); + writePolicyXml(stream, false /*forBackup*/, USER_ALL, null); mPolicyFile.finishWrite(stream); } catch (IOException e) { Slog.w(TAG, "Failed to save policy file, restoring backup", e); @@ -1250,8 +1252,8 @@ public class NotificationManagerService extends SystemService { } } - private void writePolicyXml(OutputStream stream, boolean forBackup, int userId) - throws IOException { + private void writePolicyXml(OutputStream stream, boolean forBackup, int userId, + BackupRestoreEventLogger logger) throws IOException { final TypedXmlSerializer out; if (forBackup) { out = Xml.newFastSerializer(); @@ -4119,6 +4121,24 @@ public class NotificationManagerService extends SystemService { @Override @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void allowAssistantAdjustment(String adjustmentType) { + checkCallerIsSystemOrSystemUiOrShell(); + mAssistants.allowAdjustmentType(adjustmentType); + + handleSavePolicyFile(); + } + + @Override + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void disallowAssistantAdjustment(String adjustmentType) { + checkCallerIsSystemOrSystemUiOrShell(); + mAssistants.disallowAdjustmentType(adjustmentType); + + handleSavePolicyFile(); + } + + @Override + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) public void setAdjustmentTypeSupportedState(INotificationListener token, @Adjustment.Keys String key, boolean supported) { final long identity = Binder.clearCallingIdentity(); @@ -4133,6 +4153,7 @@ public class NotificationManagerService extends SystemService { } finally { Binder.restoreCallingIdentity(identity); } + handleSavePolicyFile(); } @Override @@ -4371,8 +4392,9 @@ public class NotificationManagerService extends SystemService { List<NotificationChannel> channels = channelsList.getList(); final int channelsSize = channels.size(); ParceledListSlice<NotificationChannel> oldChannels = - mPreferencesHelper.getNotificationChannels(pkg, uid, true); - final boolean hadChannel = oldChannels != null && !oldChannels.getList().isEmpty(); + mPreferencesHelper.getNotificationChannels(pkg, uid, true, false); + final boolean hadNonBundleChannel = + oldChannels != null && !oldChannels.getList().isEmpty(); boolean needsPolicyFileChange = false; boolean hasRequestedNotificationPermission = false; for (int i = 0; i < channelsSize; i++) { @@ -4389,13 +4411,18 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), false), NOTIFICATION_CHANNEL_OR_GROUP_ADDED); - boolean hasChannel = hadChannel || hasRequestedNotificationPermission; - if (!hasChannel) { + boolean hasNonBundleChannel = + hadNonBundleChannel || hasRequestedNotificationPermission; + if (!hasNonBundleChannel) { ParceledListSlice<NotificationChannel> currChannels = - mPreferencesHelper.getNotificationChannels(pkg, uid, true); - hasChannel = currChannels != null && !currChannels.getList().isEmpty(); - } - if (!hadChannel && hasChannel && !hasRequestedNotificationPermission + mPreferencesHelper.getNotificationChannels(pkg, uid, true, false); + hasNonBundleChannel = + currChannels != null && !currChannels.getList().isEmpty(); + } + // show perm prompt if new non-bundle channel added and the user has not + // seen the prompt + if (!hadNonBundleChannel && hasNonBundleChannel + && !hasRequestedNotificationPermission && startingTaskId != ActivityTaskManager.INVALID_TASK_ID) { hasRequestedNotificationPermission = true; if (mPermissionPolicyInternal == null) { @@ -4630,7 +4657,7 @@ public class NotificationManagerService extends SystemService { public ParceledListSlice<NotificationChannel> getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { enforceSystemOrSystemUI("getNotificationChannelsForPackage"); - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted); + return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted, true); } @Override @@ -4762,7 +4789,7 @@ public class NotificationManagerService extends SystemService { /* ignore */ } return mPreferencesHelper.getNotificationChannels( - targetPkg, targetUid, false /* includeDeleted */); + targetPkg, targetUid, false /* includeDeleted */, true); } throw new SecurityException("Pkg " + callingPkg + " cannot read channels for " + targetPkg + " in " + userId); @@ -4891,7 +4918,7 @@ public class NotificationManagerService extends SystemService { throw new SecurityException("Not currently an assistant"); } - return mAssistants.getAllowedAssistantAdjustments(); + return new ArrayList<>(mAssistants.getAllowedAssistantAdjustments()); } /** @@ -6171,7 +6198,7 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); final ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { - writePolicyXml(baos, true /*forBackup*/, user); + writePolicyXml(baos, true /*forBackup*/, user, null); return baos.toByteArray(); } catch (IOException e) { Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); @@ -6190,7 +6217,7 @@ public class NotificationManagerService extends SystemService { } final ByteArrayInputStream bais = new ByteArrayInputStream(payload); try { - readPolicyXml(bais, true /*forRestore*/, user); + readPolicyXml(bais, true /*forRestore*/, user, null); handleSavePolicyFile(); } catch (NumberFormatException | XmlPullParserException | IOException e) { Slog.w(TAG, "applyRestore: error reading payload", e); @@ -6631,7 +6658,7 @@ public class NotificationManagerService extends SystemService { verifyPrivilegedListener(token, user, true); return mPreferencesHelper.getNotificationChannels(pkg, - getUidForPackageAndUser(pkg, user), false /* includeDeleted */); + getUidForPackageAndUser(pkg, user), false /* includeDeleted */, true); } @Override @@ -7392,6 +7419,37 @@ public class NotificationManagerService extends SystemService { */ private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { + public byte[] getBackupPayload(int user, BackupRestoreEventLogger logger) { + checkCallerIsSystem(); + if (DBG) Slog.d(TAG, "getBackupPayload u=" + user); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + writePolicyXml(baos, true /*forBackup*/, user, logger); + return baos.toByteArray(); + } catch (IOException e) { + Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + } + return null; + } + + @Override + public void applyRestore(byte[] payload, int user, BackupRestoreEventLogger logger) { + checkCallerIsSystem(); + if (DBG) Slog.d(TAG, "applyRestore u=" + user + " payload=" + + (payload != null ? new String(payload, StandardCharsets.UTF_8) : null)); + if (payload == null) { + Slog.w(TAG, "applyRestore: no payload to restore for user " + user); + return; + } + final ByteArrayInputStream bais = new ByteArrayInputStream(payload); + try { + readPolicyXml(bais, true /*forRestore*/, user, logger); + handleSavePolicyFile(); + } catch (NumberFormatException | XmlPullParserException | IOException e) { + Slog.w(TAG, "applyRestore: error reading payload", e); + } + } + @Override public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId) { @@ -7641,8 +7699,9 @@ public class NotificationManagerService extends SystemService { } int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { - return mPreferencesHelper.getNotificationChannels(pkg, uid, includeDeleted).getList() - .size(); + // don't show perm prompt if the only channels are bundle channels + return mPreferencesHelper.getNotificationChannels( + pkg, uid, includeDeleted, false).getList().size(); } void cancelNotificationInternal(String pkg, String opPkg, int callingUid, int callingPid, @@ -7953,11 +8012,16 @@ public class NotificationManagerService extends SystemService { } /** - * Returns a channel, if exists, and restores deleted conversation channels. + * Returns a channel, if exists and is not a bundle channel, and restores deleted + * conversation channels. */ @Nullable private NotificationChannel getNotificationChannelRestoreDeleted(String pkg, int callingUid, int notificationUid, String channelId, String conversationId) { + if (SYSTEM_RESERVED_IDS.contains(channelId)) { + // apps cannot post to these channels directly, in case they post incorrect content + return null; + } // Restore a deleted conversation channel, if exists. Otherwise use the parent channel. NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel( pkg, notificationUid, channelId, conversationId, @@ -11395,6 +11459,7 @@ public class NotificationManagerService extends SystemService { static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants"; private static final String ATT_TYPES = "types"; + private static final String ATT_DENIED = "denied_adjustments"; private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments"; private final Object mLock = new Object(); @@ -11403,6 +11468,9 @@ public class NotificationManagerService extends SystemService { private Set<String> mAllowedAdjustments = new ArraySet<>(); @GuardedBy("mLock") + private Set<String> mDeniedAdjustments = new ArraySet<>(); + + @GuardedBy("mLock") private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>(); protected ComponentName mDefaultFromConfig = null; @@ -11474,9 +11542,11 @@ public class NotificationManagerService extends SystemService { IPackageManager pm) { super(context, lock, up, pm); - // Add all default allowed adjustment types. - for (int i = 0; i < ALLOWED_ADJUSTMENTS.length; i++) { - mAllowedAdjustments.add(ALLOWED_ADJUSTMENTS[i]); + if (!notificationClassification()) { + // Add all default allowed adjustment types. + for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) { + mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]); + } } } @@ -11539,17 +11609,28 @@ public class NotificationManagerService extends SystemService { return android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE; } - protected List<String> getAllowedAssistantAdjustments() { + protected Set<String> getAllowedAssistantAdjustments() { synchronized (mLock) { - List<String> types = new ArrayList<>(); - types.addAll(mAllowedAdjustments); - return types; + if (notificationClassification()) { + Set<String> types = new HashSet<>(Set.of(DEFAULT_ALLOWED_ADJUSTMENTS)); + types.removeAll(mDeniedAdjustments); + return types; + } else { + Set<String> types = new HashSet<>(); + types.addAll(mAllowedAdjustments); + return types; + } } } protected boolean isAdjustmentAllowed(String type) { synchronized (mLock) { - return mAllowedAdjustments.contains(type); + if (notificationClassification()) { + return List.of(DEFAULT_ALLOWED_ADJUSTMENTS).contains(type) + && !mDeniedAdjustments.contains(type); + } else { + return mAllowedAdjustments.contains(type); + } } } @@ -11911,6 +11992,30 @@ public class NotificationManagerService extends SystemService { @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) @GuardedBy("mNotificationLock") + public void allowAdjustmentType(@Adjustment.Keys String key) { + if (!android.service.notification.Flags.notificationClassification()) { + return; + } + mDeniedAdjustments.remove(key); + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + mHandler.post(() -> notifyCapabilitiesChanged(info)); + } + } + + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + @GuardedBy("mNotificationLock") + public void disallowAdjustmentType(@Adjustment.Keys String key) { + if (!android.service.notification.Flags.notificationClassification()) { + return; + } + mDeniedAdjustments.add(key); + for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { + mHandler.post(() -> notifyCapabilitiesChanged(info)); + } + } + + @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + @GuardedBy("mNotificationLock") public void setAdjustmentTypeSupportedState(ManagedServiceInfo info, @Adjustment.Keys String key, boolean supported) { if (!android.service.notification.Flags.notificationClassification()) { @@ -11965,6 +12070,43 @@ public class NotificationManagerService extends SystemService { } } } + + @Override + protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException { + if (!android.service.notification.Flags.notificationClassification()) { + return; + } + synchronized (mLock) { + out.startTag(null, ATT_DENIED); + out.attribute(null, ATT_TYPES, TextUtils.join(",", mDeniedAdjustments)); + out.endTag(null, ATT_DENIED); + } + } + + @Override + protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { + if (!android.service.notification.Flags.notificationClassification()) { + return; + } + if (ATT_DENIED.equals(tag)) { + final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); + synchronized (mLock) { + mDeniedAdjustments.clear(); + if (!TextUtils.isEmpty(types)) { + mDeniedAdjustments.addAll(Arrays.asList(types.split(","))); + } + } + } + } + + private void notifyCapabilitiesChanged(final ManagedServiceInfo info) { + final INotificationListener assistant = (INotificationListener) info.service; + try { + assistant.onAllowedAdjustmentsChanged(); + } catch (RemoteException ex) { + Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex); + } + } } /** diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 3ba93845a290..93f512bc7e17 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -334,7 +334,7 @@ public final class NotificationRecord { return helper.createWaveformVibration(vibrationPattern, insistent); } - if (com.android.server.notification.Flags.notificationVibrationInSoundUri()) { + if (com.android.server.notification.Flags.notificationVibrationInSoundUriForChannel()) { final VibrationEffect vibrationEffectFromSoundUri = helper.createVibrationEffectFromSoundUri(channel.getSound()); if (vibrationEffectFromSoundUri != null) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 3349b1308c3f..c9edc4106943 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1870,7 +1870,7 @@ public class PreferencesHelper implements RankingConfig { @Override public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, - boolean includeDeleted) { + boolean includeDeleted, boolean includeBundles) { Objects.requireNonNull(pkg); List<NotificationChannel> channels = new ArrayList<>(); synchronized (mLock) { @@ -1882,7 +1882,9 @@ public class PreferencesHelper implements RankingConfig { for (int i = 0; i < N; i++) { final NotificationChannel nc = r.channels.valueAt(i); if (includeDeleted || !nc.isDeleted()) { - channels.add(nc); + if (includeBundles || !SYSTEM_RESERVED_IDS.contains(nc.getId())) { + channels.add(nc); + } } } return new ParceledListSlice<>(channels); diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 8df24c9911a6..001e91cbea12 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -55,5 +55,5 @@ public interface RankingConfig { void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannels(String pkg, int uid); ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, - boolean includeDeleted); + boolean includeDeleted, boolean includeBundles); } diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 6efe88f6a155..24b090c6ffab 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Binder; +import android.os.UserHandle; import android.provider.Settings; import android.service.notification.Condition; import android.service.notification.ScheduleCalendar; @@ -115,6 +116,11 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } @Override + public void onUserSwitched(UserHandle user) { + // Nothing to do because time-based schedules are not tied to any user data. + } + + @Override public void onDestroy() { super.onDestroy(); if (DEBUG) Slog.d(TAG, "onDestroy"); diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java index 97073b77f1f7..656f9dfd0917 100644 --- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java +++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java @@ -19,6 +19,7 @@ package com.android.server.notification; import android.content.ComponentName; import android.content.Context; import android.net.Uri; +import android.os.UserHandle; import android.service.notification.ConditionProviderService; import android.service.notification.IConditionProvider; import android.util.TimeUtils; @@ -30,9 +31,10 @@ import java.util.Date; public abstract class SystemConditionProviderService extends ConditionProviderService { - abstract public void dump(PrintWriter pw, DumpFilter filter); - abstract public boolean isValidConditionId(Uri id); - abstract public void onBootComplete(); + public abstract void dump(PrintWriter pw, DumpFilter filter); + public abstract boolean isValidConditionId(Uri id); + public abstract void onBootComplete(); + public abstract void onUserSwitched(UserHandle user); final ComponentName getComponent() { return new ComponentName("android", this.getClass().getName()); diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 50bfbc3530a9..b1f010c38d4e 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -102,16 +102,6 @@ public class ZenModeConditions implements ConditionProviders.Callback { } @Override - public void onBootComplete() { - // noop - } - - @Override - public void onUserSwitched() { - // noop - } - - @Override public void onServiceAdded(ComponentName component) { if (DEBUG) Log.d(TAG, "onServiceAdded " + component); final int callingUid = Binder.getCallingUid(); diff --git a/services/core/java/com/android/server/pinner/PinnerService.java b/services/core/java/com/android/server/pinner/PinnerService.java index d7ac5203ff53..2c75926c4943 100644 --- a/services/core/java/com/android/server/pinner/PinnerService.java +++ b/services/core/java/com/android/server/pinner/PinnerService.java @@ -121,9 +121,6 @@ public final class PinnerService extends SystemService { private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean("pinner.use_pinlist", true); - private static final int MAX_CAMERA_PIN_SIZE = 80 * (1 << 20); // 80MB max for camera app. - private static final int MAX_ASSISTANT_PIN_SIZE = 60 * (1 << 20); // 60MB max for assistant app. - public static final String ANON_REGION_STAT_NAME = "[anon]"; private static final String SYSTEM_GROUP_NAME = "system"; @@ -179,8 +176,10 @@ public final class PinnerService extends SystemService { // Resource-configured pinner flags; private final boolean mConfiguredToPinCamera; + private final int mConfiguredCameraPinBytes; private final int mConfiguredHomePinBytes; private final boolean mConfiguredToPinAssistant; + private final int mConfiguredAssistantPinBytes; private final int mConfiguredWebviewPinBytes; // This is the percentage of total device memory that will be used to set the global quota. @@ -250,6 +249,10 @@ public final class PinnerService extends SystemService { mDeviceConfigInterface = mInjector.getDeviceConfigInterface(); mConfiguredToPinCamera = context.getResources().getBoolean( com.android.internal.R.bool.config_pinnerCameraApp); + mConfiguredCameraPinBytes = context.getResources().getInteger( + com.android.internal.R.integer.config_pinnerCameraPinBytes); + mConfiguredAssistantPinBytes = context.getResources().getInteger( + com.android.internal.R.integer.config_pinnerAssistantPinBytes); mConfiguredHomePinBytes = context.getResources().getInteger( com.android.internal.R.integer.config_pinnerHomePinBytes); mConfiguredToPinAssistant = context.getResources().getBoolean( @@ -812,11 +815,11 @@ public final class PinnerService extends SystemService { private int getSizeLimitForKey(@AppKey int key) { switch (key) { case KEY_CAMERA: - return MAX_CAMERA_PIN_SIZE; + return mConfiguredCameraPinBytes; case KEY_HOME: return mConfiguredHomePinBytes; case KEY_ASSISTANT: - return MAX_ASSISTANT_PIN_SIZE; + return mConfiguredAssistantPinBytes; default: return 0; } @@ -1303,6 +1306,10 @@ public final class PinnerService extends SystemService { pw.format(" Maximum Pinner quota: %d bytes (%.2f MB)\n", mConfiguredMaxPinnedMemory, mConfiguredMaxPinnedMemory / bytesPerMB); pw.format(" Max Home App Pin Bytes (without deps): %d\n", mConfiguredHomePinBytes); + pw.format(" Max Assistant App Pin Bytes (without deps): %d\n", + mConfiguredAssistantPinBytes); + pw.format( + " Max Camera App Pin Bytes (without deps): %d\n", mConfiguredCameraPinBytes); pw.format("\nPinned Files:\n"); synchronized (PinnerService.this) { long totalSize = 0; diff --git a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java index 49a6ffde6783..a221152222ee 100644 --- a/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java +++ b/services/core/java/com/android/server/pm/BackgroundUserSoundNotifier.java @@ -55,6 +55,8 @@ public class BackgroundUserSoundNotifier { private static final String ACTION_SWITCH_USER = "com.android.server.ACTION_SWITCH_TO_USER"; private static final String ACTION_DISMISS_NOTIFICATION = "com.android.server.ACTION_DISMISS_NOTIFICATION"; + private static final String EXTRA_NOTIFICATION_CLIENT_UID = + "com.android.server.EXTRA_CLIENT_UID"; /** * The clientUid from the AudioFocusInfo of the background user, * for which an active notification is currently displayed. @@ -101,7 +103,7 @@ public class BackgroundUserSoundNotifier { ActivityManager am = mSystemUserContext.getSystemService(ActivityManager.class); registerReceiver(am); - mBgUserListener = new BackgroundUserListener(mSystemUserContext); + mBgUserListener = new BackgroundUserListener(); AudioPolicy.Builder focusControlPolicyBuilder = new AudioPolicy.Builder(mSystemUserContext); focusControlPolicyBuilder.setLooper(Looper.getMainLooper()); @@ -119,26 +121,16 @@ public class BackgroundUserSoundNotifier { final class BackgroundUserListener extends AudioPolicy.AudioPolicyFocusListener { - Context mSystemContext; - - BackgroundUserListener(Context systemContext) { - mSystemContext = systemContext; - } - - @SuppressLint("MissingPermission") public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) { try { - BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi, - mSystemContext.createContextAsUser( - UserHandle.of(ActivityManager.getCurrentUser()), 0)); + BackgroundUserSoundNotifier.this.notifyForegroundUserAboutSoundIfNecessary(afi); } catch (RemoteException e) { throw new RuntimeException(e); } } - @SuppressLint("MissingPermission") public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) { - BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(); + BackgroundUserSoundNotifier.this.dismissNotificationIfNecessary(afi.getClientUid()); } } @@ -160,7 +152,7 @@ public class BackgroundUserSoundNotifier { if (mNotificationClientUid == -1) { return; } - dismissNotification(); + dismissNotification(mNotificationClientUid); if (DEBUG) { final int actionIndex = intent.getAction().lastIndexOf(".") + 1; @@ -171,7 +163,7 @@ public class BackgroundUserSoundNotifier { } if (ACTION_MUTE_SOUND.equals(intent.getAction())) { - muteAlarmSounds(mSystemUserContext); + muteAlarmSounds(mNotificationClientUid); } else if (ACTION_SWITCH_USER.equals(intent.getAction())) { activityManager.switchUser(UserHandle.getUserId(mNotificationClientUid)); } @@ -193,17 +185,17 @@ public class BackgroundUserSoundNotifier { */ @SuppressLint("MissingPermission") @VisibleForTesting - void muteAlarmSounds(Context context) { - AudioManager audioManager = context.getSystemService(AudioManager.class); + void muteAlarmSounds(int notificationClientUid) { + AudioManager audioManager = mSystemUserContext.getSystemService(AudioManager.class); if (audioManager != null) { for (AudioPlaybackConfiguration apc : audioManager.getActivePlaybackConfigurations()) { - if (apc.getClientUid() == mNotificationClientUid && apc.getPlayerProxy() != null) { + if (apc.getClientUid() == notificationClientUid && apc.getPlayerProxy() != null) { apc.getPlayerProxy().stop(); } } } - AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(); + AudioFocusInfo currentAfi = getAudioFocusInfoForNotification(notificationClientUid); if (currentAfi != null) { mFocusControlAudioPolicy.sendFocusLossAndUpdate(currentAfi); } @@ -212,9 +204,14 @@ public class BackgroundUserSoundNotifier { /** * Check if sound is coming from background user and show notification is required. */ + @SuppressLint("MissingPermission") @VisibleForTesting - void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi, Context foregroundContext) - throws RemoteException { + void notifyForegroundUserAboutSoundIfNecessary(AudioFocusInfo afi) throws RemoteException { + if (afi == null) { + return; + } + Context foregroundContext = mSystemUserContext.createContextAsUser( + UserHandle.of(ActivityManager.getCurrentUser()), 0); final int userId = UserHandle.getUserId(afi.getClientUid()); final int usage = afi.getAttributes().getUsage(); UserInfo userInfo = mUserManager.getUserInfo(userId); @@ -232,8 +229,8 @@ public class BackgroundUserSoundNotifier { mNotificationClientUid = afi.getClientUid(); - mNotificationManager.notifyAsUser(LOG_TAG, mNotificationClientUid, - createNotification(userInfo.name, foregroundContext), + mNotificationManager.notifyAsUser(LOG_TAG, afi.getClientUid(), + createNotification(userInfo.name, foregroundContext, afi.getClientUid()), foregroundContext.getUser()); } } @@ -245,14 +242,15 @@ public class BackgroundUserSoundNotifier { * focus ownership. */ @VisibleForTesting - void dismissNotificationIfNecessary() { - if (getAudioFocusInfoForNotification() == null && mNotificationClientUid >= 0) { + void dismissNotificationIfNecessary(int notificationClientUid) { + if (getAudioFocusInfoForNotification(notificationClientUid) == null + && mNotificationClientUid >= 0) { if (DEBUG) { Log.d(LOG_TAG, "Alarm ringing on background user " - + UserHandle.getUserHandleForUid(mNotificationClientUid).getIdentifier() + + UserHandle.getUserHandleForUid(notificationClientUid).getIdentifier() + " left focus stack, dismissing notification"); } - dismissNotification(); + dismissNotification(notificationClientUid); mNotificationClientUid = -1; } } @@ -262,8 +260,8 @@ public class BackgroundUserSoundNotifier { * shown. */ @SuppressLint("MissingPermission") - private void dismissNotification() { - mNotificationManager.cancelAsUser(LOG_TAG, mNotificationClientUid, UserHandle.ALL); + private void dismissNotification(int notificationClientUid) { + mNotificationManager.cancelAsUser(LOG_TAG, notificationClientUid, UserHandle.ALL); } /** @@ -272,11 +270,11 @@ public class BackgroundUserSoundNotifier { @SuppressLint("MissingPermission") @VisibleForTesting @Nullable - AudioFocusInfo getAudioFocusInfoForNotification() { - if (mNotificationClientUid >= 0) { + AudioFocusInfo getAudioFocusInfoForNotification(int notificationClientUid) { + if (notificationClientUid >= 0) { List<AudioFocusInfo> stack = mFocusControlAudioPolicy.getFocusStack(); for (int i = stack.size() - 1; i >= 0; i--) { - if (stack.get(i).getClientUid() == mNotificationClientUid) { + if (stack.get(i).getClientUid() == notificationClientUid) { return stack.get(i); } } @@ -284,22 +282,24 @@ public class BackgroundUserSoundNotifier { return null; } - private PendingIntent createPendingIntent(String intentAction) { + private PendingIntent createPendingIntent(String intentAction, int notificationClientUid) { final Intent intent = new Intent(intentAction); - PendingIntent resultPI = PendingIntent.getBroadcast(mSystemUserContext, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - return resultPI; + intent.putExtra(EXTRA_NOTIFICATION_CLIENT_UID, notificationClientUid); + return PendingIntent.getBroadcast(mSystemUserContext, notificationClientUid, intent, + PendingIntent.FLAG_IMMUTABLE); } + @SuppressLint("MissingPermission") @VisibleForTesting - Notification createNotification(String userName, Context fgContext) { + Notification createNotification(String userName, Context fgContext, int notificationClientUid) { final String title = fgContext.getString(R.string.bg_user_sound_notification_title_alarm, userName); final int icon = R.drawable.ic_audio_alarm; - PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND); - PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER); - PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION); + PendingIntent mutePI = createPendingIntent(ACTION_MUTE_SOUND, notificationClientUid); + PendingIntent switchPI = createPendingIntent(ACTION_SWITCH_USER, notificationClientUid); + PendingIntent dismissNotificationPI = createPendingIntent(ACTION_DISMISS_NOTIFICATION, + notificationClientUid); final Notification.Action mute = new Notification.Action.Builder(null, fgContext.getString(R.string.bg_user_sound_notification_button_mute), diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index be7631d2a868..19ac1ecc9314 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -5145,28 +5145,10 @@ public class ComputerEngine implements Computer { } updateOwnerPackageName = installSource.mUpdateOwnerPackageName; - - if (DEBUG_INSTALL) { - Log.d(TAG, "ComputerEngine getInstallSourceInfo updateOwnerPackageName = " - + updateOwnerPackageName + ", callingUid = " + callingUid + ", packageName = " - + packageName + ", userId = " + userId); - } - if (updateOwnerPackageName != null) { final PackageStateInternal ps = mSettings.getPackage(updateOwnerPackageName); final boolean isCallerSystemOrUpdateOwner = callingUid == Process.SYSTEM_UID || isCallerSameApp(updateOwnerPackageName, callingUid); - - if (DEBUG_INSTALL) { - Log.d(TAG, "ComputerEngine getInstallSourceInfo ps = " - + ps + ", isCallerSystemOrUpdateOwner =" + isCallerSystemOrUpdateOwner - + ", isCallerSameApp = " - + isCallerSameApp(updateOwnerPackageName, callingUid) + ", filter = " - + shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId) - + ", FromManagedUserOrProfile = " - + isCallerFromManagedUserOrProfile(userId)); - } - // Except for package visibility filtering, we also hide update owner if the installer // is in the managed user or profile. As we don't enforce the update ownership for the // managed user and profile, knowing there's an update owner is meaningless in that @@ -5178,11 +5160,6 @@ public class ComputerEngine implements Computer { } } - if (DEBUG_INSTALL) { - Log.d(TAG, "ComputerEngine getInstallSourceInfo updateOwnerPackageName = " - + updateOwnerPackageName); - } - if (installSource.mIsInitiatingPackageUninstalled) { // We can't check visibility in the usual way, since the initiating package is no // longer present. So we apply simpler rules to whether to expose the info: diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 02afdd662b10..9e8598a04a98 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -89,6 +89,7 @@ import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -893,7 +894,8 @@ public final class DexOptHelper { @Override public void onApexStaged(@NonNull ApexStagedEvent event) { - mArtManager.onApexStaged(event.stagedApexModuleNames); + mArtManager.onApexStaged(Arrays.stream(event.stagedApexInfos) + .map(info -> info.moduleName).toArray(String[]::new)); } } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index eb7c24399bba..9e0ba8492ab9 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1103,17 +1103,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isUpdateOwnershipEnforcementEnabled = mPm.isUpdateOwnershipEnforcementAvailable() && existingUpdateOwnerPackageName != null; - - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + " isUpdateOwnershipEnforcementEnabled= " + isUpdateOwnershipEnforcementEnabled - + ", mPm.isUpdateOwnershipEnforcementAvailable= " - + mPm.isUpdateOwnershipEnforcementAvailable() - + ", existingUpdateOwnerPackageName=" + existingUpdateOwnerPackageName - + ", isUpdateOwner= " + isUpdateOwner + ", getInstallerPackageName()= " - + getInstallerPackageName() + ", isInstallerShell= " + isInstallerShell - + ", mInstallerUid=" + mInstallerUid + ", packageName = " + packageName); - } // For an installation that un-archives an app, if the installer doesn't have the // INSTALL_PACKAGES permission, the user should have already been prompted to confirm the // un-archive request. There's no need for another confirmation during the installation. @@ -1127,10 +1116,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { || isInstallUnarchive; if (noUserActionNecessary) { - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + " noUserActionNecessary userActionNotTypicallyNeededResponse"); - } return userActionNotTypicallyNeededResponse; } @@ -1140,27 +1125,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { && !isInstallerShell // We don't enforce the update ownership for the managed user and profile. && !isFromManagedUserOrProfile) { - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + "USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER"); - } return USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER; } if (isPermissionGranted) { - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + " permission userActionNotTypicallyNeededResponse"); - } return userActionNotTypicallyNeededResponse; } if (snapshot.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) { - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + " disable USER_ACTION_REQUIRED"); - } // show the installer to account for device policy or unknown sources use cases return USER_ACTION_REQUIRED; } @@ -1169,17 +1142,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { && isUpdateWithoutUserActionPermissionGranted && ((isUpdateOwnershipEnforcementEnabled ? isUpdateOwner : isInstallerOfRecord) || isSelfUpdate)) { - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + " USER_ACTION_PENDING_APK_PARSING"); - } return USER_ACTION_PENDING_APK_PARSING; } - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession computeUserActionRequirement" - + " USER_ACTION_REQUIRED"); - } return USER_ACTION_REQUIRED; } @@ -2750,11 +2715,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @UserActionRequirement int userActionRequirement = USER_ACTION_NOT_NEEDED; // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc userActionRequirement = session.computeUserActionRequirement(); - if (Build.IS_USERDEBUG) { - Log.d("updateowner", "PackageInstallerSession checkUserActionRequirement" - + " userActionRequirement= " + userActionRequirement - + ", session.packageName= " + session.getPackageName()); - } session.updateUserActionRequirement(userActionRequirement); if (userActionRequirement == USER_ACTION_REQUIRED || userActionRequirement == USER_ACTION_REQUIRED_UPDATE_OWNER_REMINDER) { diff --git a/services/core/java/com/android/server/pm/PackageManagerNative.java b/services/core/java/com/android/server/pm/PackageManagerNative.java index 66ecd6e67e56..7d8573e35522 100644 --- a/services/core/java/com/android/server/pm/PackageManagerNative.java +++ b/services/core/java/com/android/server/pm/PackageManagerNative.java @@ -20,7 +20,6 @@ import static android.content.pm.PackageManager.CERT_INPUT_SHA256; import static com.android.server.pm.PackageManagerService.TAG; -import android.annotation.Nullable; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManagerNative; import android.content.pm.IStagedApexObserver; @@ -184,14 +183,8 @@ final class PackageManagerNative extends IPackageManagerNative.Stub { } @Override - public String[] getStagedApexModuleNames() { - return mPm.mInstallerService.getStagingManager() - .getStagedApexModuleNames().toArray(new String[0]); - } - - @Override - @Nullable - public StagedApexInfo getStagedApexInfo(String moduleName) { - return mPm.mInstallerService.getStagingManager().getStagedApexInfo(moduleName); + public StagedApexInfo[] getStagedApexInfos() { + return mPm.mInstallerService.getStagingManager().getStagedApexInfos().toArray( + new StagedApexInfo[0]); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c8cf938099f4..455776993c56 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -204,7 +204,6 @@ import com.android.server.FgThread; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; import com.android.server.LockGuard; -import com.android.server.PackageWatchdog; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.ThreadPriorityBooster; @@ -214,6 +213,7 @@ import com.android.server.art.DexUseManagerLocal; import com.android.server.art.model.DeleteResult; import com.android.server.compat.CompatChange; import com.android.server.compat.PlatformCompat; +import com.android.server.crashrecovery.CrashRecoveryAdaptor; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.Settings.VersionInfo; import com.android.server.pm.dex.ArtManagerService; @@ -340,7 +340,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService static final boolean DEBUG_UPGRADE = false; static final boolean DEBUG_DOMAIN_VERIFICATION = false; static final boolean DEBUG_BACKUP = false; - public static final boolean DEBUG_INSTALL = Build.IS_USERDEBUG; + public static final boolean DEBUG_INSTALL = false; public static final boolean DEBUG_REMOVE = false; static final boolean DEBUG_PACKAGE_INFO = false; static final boolean DEBUG_INTENT_MATCHING = false; @@ -3048,7 +3048,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mDexManager.writePackageDexUsageNow(); mDynamicCodeLogger.writeNow(); if (!refactorCrashrecovery()) { - PackageWatchdog.getInstance(mContext).writeNow(); + CrashRecoveryAdaptor.packageWatchdogWriteNow(mContext); } synchronized (mLock) { diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 94bdfbd9c6f5..38cf0b9136dd 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -17,7 +17,6 @@ package com.android.server.pm; import android.annotation.NonNull; -import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; @@ -38,7 +37,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.Slog; @@ -65,9 +63,9 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -807,14 +805,13 @@ public class StagingManager { } /** - * Returns ApexInfo about APEX contained inside the session as a {@code Map<String, ApexInfo>}, - * where the key of the map is the module name of the ApexInfo. + * Returns ApexInfo about APEX contained inside the session. * - * Returns an empty map if there is any error. + * Returns an empty list if there is any error. */ @VisibleForTesting @NonNull - Map<String, ApexInfo> getStagedApexInfos(@NonNull StagedSession session) { + List<ApexInfo> getStagedApexInfos(@NonNull StagedSession session) { Preconditions.checkArgument(session != null, "Session is null"); Preconditions.checkArgument(!session.hasParentSessionId(), session.sessionId() + " session has parent session"); @@ -824,7 +821,7 @@ public class StagingManager { // Even if caller calls this method on ready session, the session could be abandoned // right after this method is called. if (!session.isSessionReady() || session.isDestroyed()) { - return Collections.emptyMap(); + return Collections.emptyList(); } ApexSessionParams params = new ApexSessionParams(); @@ -838,38 +835,17 @@ public class StagingManager { } } params.childSessionIds = childSessionIds.toArray(); - - ApexInfo[] infos = mApexManager.getStagedApexInfos(params); - Map<String, ApexInfo> result = new ArrayMap<>(); - for (ApexInfo info : infos) { - result.put(info.moduleName, info); - } - return result; + return Arrays.asList(mApexManager.getStagedApexInfos(params)); } /** - * Returns apex module names of all packages that are staged ready - */ - List<String> getStagedApexModuleNames() { - List<String> result = new ArrayList<>(); - synchronized (mStagedSessions) { - for (int i = 0; i < mStagedSessions.size(); i++) { - final StagedSession session = mStagedSessions.valueAt(i); - if (!session.isSessionReady() || session.isDestroyed() - || session.hasParentSessionId() || !session.containsApexSession()) { - continue; - } - result.addAll(getStagedApexInfos(session).keySet()); - } - } - return result; - } - - /** - * Returns ApexInfo of the {@code moduleInfo} provided if it is staged, otherwise returns null. + * Returns ApexInfo list about APEXes contained inside all staged sessions. + * + * Returns an empty list if there is any error. */ - @Nullable - StagedApexInfo getStagedApexInfo(String moduleName) { + @NonNull + List<StagedApexInfo> getStagedApexInfos() { + List<StagedApexInfo> result = new ArrayList<>(); synchronized (mStagedSessions) { for (int i = 0; i < mStagedSessions.size(); i++) { final StagedSession session = mStagedSessions.valueAt(i); @@ -877,8 +853,7 @@ public class StagingManager { || session.hasParentSessionId() || !session.containsApexSession()) { continue; } - ApexInfo ai = getStagedApexInfos(session).get(moduleName); - if (ai != null) { + getStagedApexInfos(session).stream().map(ai -> { StagedApexInfo info = new StagedApexInfo(); info.moduleName = ai.moduleName; info.diskImagePath = ai.modulePath; @@ -886,17 +861,19 @@ public class StagingManager { info.versionName = ai.versionName; info.hasClassPathJars = ai.hasClassPathJars; return info; - } + }).forEach(result::add); } } - return null; + return result; } private void notifyStagedApexObservers() { synchronized (mStagedApexObservers) { + List<StagedApexInfo> stagedApexInfos = getStagedApexInfos(); + ApexStagedEvent event = new ApexStagedEvent(); + event.stagedApexInfos = + stagedApexInfos.toArray(new StagedApexInfo[stagedApexInfos.size()]); for (IStagedApexObserver observer : mStagedApexObservers) { - ApexStagedEvent event = new ApexStagedEvent(); - event.stagedApexModuleNames = getStagedApexModuleNames().toArray(new String[0]); try { observer.onApexStaged(event); } catch (RemoteException re) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 708e0679d6d4..daf413bb30b3 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1101,23 +1101,14 @@ public class UserManagerService extends IUserManager.Stub { if (android.multiuser.Flags.cachesNotInvalidatedAtStartReadOnly()) { UserManager.invalidateIsUserUnlockedCache(); UserManager.invalidateQuietModeEnabledCache(); + if (android.multiuser.Flags.cacheUserPropertiesCorrectlyReadOnly()) { + UserManager.invalidateStaticUserProperties(); + UserManager.invalidateUserPropertiesCache(); + } UserManager.invalidateCacheOnUserListChange(); } } - private boolean doesDeviceHardwareSupportPrivateSpace() { - return !mPm.hasSystemFeature(FEATURE_EMBEDDED, 0) - && !mPm.hasSystemFeature(FEATURE_WATCH, 0) - && !mPm.hasSystemFeature(FEATURE_LEANBACK, 0) - && !mPm.hasSystemFeature(FEATURE_AUTOMOTIVE, 0); - } - - private static boolean isAutoLockForPrivateSpaceEnabled() { - return android.os.Flags.allowPrivateProfile() - && Flags.supportAutolockForPrivateSpace() - && android.multiuser.Flags.enablePrivateSpaceFeatures(); - } - void systemReady() { mAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); @@ -1162,6 +1153,19 @@ public class UserManagerService extends IUserManager.Stub { } } + private boolean doesDeviceHardwareSupportPrivateSpace() { + return !mPm.hasSystemFeature(FEATURE_EMBEDDED, 0) + && !mPm.hasSystemFeature(FEATURE_WATCH, 0) + && !mPm.hasSystemFeature(FEATURE_LEANBACK, 0) + && !mPm.hasSystemFeature(FEATURE_AUTOMOTIVE, 0); + } + + private static boolean isAutoLockForPrivateSpaceEnabled() { + return android.os.Flags.allowPrivateProfile() + && Flags.supportAutolockForPrivateSpace() + && android.multiuser.Flags.enablePrivateSpaceFeatures(); + } + private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() { return android.os.Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceAutolockOnRestarts() diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index eb62b5631c43..8ba56c5320f2 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -771,6 +771,10 @@ public class Notifier { public void onGroupRemoved(int groupId) { mInteractivityByGroupId.remove(groupId); mWakefulnessSessionObserver.removePowerGroup(groupId); + if (mFlags.isPerDisplayWakeByTouchEnabled()) { + resetDisplayInteractivities(); + mInputManagerInternal.setDisplayInteractivities(mDisplayInteractivities); + } } /** diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java index 685ab3a9424f..ab756f2a755b 100644 --- a/services/core/java/com/android/server/rollback/Rollback.java +++ b/services/core/java/com/android/server/rollback/Rollback.java @@ -54,7 +54,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; -import com.android.server.RescueParty; +import com.android.server.crashrecovery.CrashRecoveryAdaptor; import com.android.server.pm.pkg.AndroidPackage; import java.io.File; @@ -627,7 +627,7 @@ class Rollback { if (!deprecateFlagsAndSettingsResets()) { // Clear flags. - RescueParty.resetDeviceConfigForPackages(packageNames); + CrashRecoveryAdaptor.rescuePartyResetDeviceConfigForPackages(packageNames); } Consumer<Intent> onResult = result -> { diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java index eb5361c84b89..d1610704a6d2 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java @@ -30,7 +30,7 @@ public class CasResource { * Handle of the current resource. Should not be changed and should be aligned with the driver * level implementation. */ - final int mHandle; + final long mHandle; private final int mSystemId; @@ -50,7 +50,7 @@ public class CasResource { this.mAvailableSessionNum = builder.mMaxSessionNum; } - public int getHandle() { + public long getHandle() { return mHandle; } @@ -146,11 +146,11 @@ public class CasResource { */ public static class Builder { - private final int mHandle; + private final long mHandle; private int mSystemId; protected int mMaxSessionNum; - Builder(int handle, int systemId) { + Builder(long handle, int systemId) { this.mHandle = handle; this.mSystemId = systemId; } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java index 5cef729627f0..b325570e35ff 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CiCamResource.java @@ -42,7 +42,7 @@ public final class CiCamResource extends CasResource { * Builder class for {@link CiCamResource}. */ public static class Builder extends CasResource.Builder { - Builder(int handle, int systemId) { + Builder(long handle, int systemId) { super(handle, systemId); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index 8e375275d080..096231910e6e 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -67,19 +67,19 @@ public final class ClientProfile { /** * The handle of the primary frontend resource */ - private int mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; + private long mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; /** * List of the frontend handles that are used by the current client. */ - private Set<Integer> mUsingFrontendHandles = new HashSet<>(); + private Set<Long> mUsingFrontendHandles = new HashSet<>(); /** * List of the client ids that share frontend with the current client. */ private Set<Integer> mShareFeClientIds = new HashSet<>(); - private Set<Integer> mUsingDemuxHandles = new HashSet<>(); + private Set<Long> mUsingDemuxHandles = new HashSet<>(); /** * Client id sharee that has shared frontend with the current client. @@ -89,7 +89,7 @@ public final class ClientProfile { /** * List of the Lnb handles that are used by the current client. */ - private Set<Integer> mUsingLnbHandles = new HashSet<>(); + private Set<Long> mUsingLnbHandles = new HashSet<>(); /** * List of the Cas system ids that are used by the current client. @@ -184,7 +184,7 @@ public final class ClientProfile { * * @param frontendHandle being used. */ - public void useFrontend(int frontendHandle) { + public void useFrontend(long frontendHandle) { mUsingFrontendHandles.add(frontendHandle); } @@ -193,14 +193,14 @@ public final class ClientProfile { * * @param frontendHandle being used. */ - public void setPrimaryFrontend(int frontendHandle) { + public void setPrimaryFrontend(long frontendHandle) { mPrimaryUsingFrontendHandle = frontendHandle; } /** * Get the primary frontend used by the client */ - public int getPrimaryFrontend() { + public long getPrimaryFrontend() { return mPrimaryUsingFrontendHandle; } @@ -222,7 +222,7 @@ public final class ClientProfile { mShareFeClientIds.remove(clientId); } - public Set<Integer> getInUseFrontendHandles() { + public Set<Long> getInUseFrontendHandles() { return mUsingFrontendHandles; } @@ -253,14 +253,14 @@ public final class ClientProfile { * * @param demuxHandle the demux being used. */ - public void useDemux(int demuxHandle) { + public void useDemux(long demuxHandle) { mUsingDemuxHandles.add(demuxHandle); } /** * Get the set of demux handles in use. */ - public Set<Integer> getInUseDemuxHandles() { + public Set<Long> getInUseDemuxHandles() { return mUsingDemuxHandles; } @@ -269,7 +269,7 @@ public final class ClientProfile { * * @param demuxHandle the demux handl being released. */ - public void releaseDemux(int demuxHandle) { + public void releaseDemux(long demuxHandle) { mUsingDemuxHandles.remove(demuxHandle); } @@ -278,11 +278,11 @@ public final class ClientProfile { * * @param lnbHandle being used. */ - public void useLnb(int lnbHandle) { + public void useLnb(long lnbHandle) { mUsingLnbHandles.add(lnbHandle); } - public Set<Integer> getInUseLnbHandles() { + public Set<Long> getInUseLnbHandles() { return mUsingLnbHandles; } @@ -291,7 +291,7 @@ public final class ClientProfile { * * @param lnbHandle being released. */ - public void releaseLnb(int lnbHandle) { + public void releaseLnb(long lnbHandle) { mUsingLnbHandles.remove(lnbHandle); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java index df735659c0fe..14bc216d03ef 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/DemuxResource.java @@ -69,7 +69,7 @@ public final class DemuxResource extends TunerResourceBasic { public static class Builder extends TunerResourceBasic.Builder { private int mFilterTypes; - Builder(int handle) { + Builder(long handle) { super(handle); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java index 7ef75e3120c5..953d97499c41 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java @@ -42,7 +42,7 @@ public final class FrontendResource extends TunerResourceBasic { /** * An array to save all the FE handles under the same exclisive group. */ - private Set<Integer> mExclusiveGroupMemberHandles = new HashSet<>(); + private Set<Long> mExclusiveGroupMemberHandles = new HashSet<>(); private FrontendResource(Builder builder) { super(builder); @@ -58,7 +58,7 @@ public final class FrontendResource extends TunerResourceBasic { return mExclusiveGroupId; } - public Set<Integer> getExclusiveGroupMemberFeHandles() { + public Set<Long> getExclusiveGroupMemberFeHandles() { return mExclusiveGroupMemberHandles; } @@ -67,7 +67,7 @@ public final class FrontendResource extends TunerResourceBasic { * * @param handle the handle to be added. */ - public void addExclusiveGroupMemberFeHandle(int handle) { + public void addExclusiveGroupMemberFeHandle(long handle) { mExclusiveGroupMemberHandles.add(handle); } @@ -76,7 +76,7 @@ public final class FrontendResource extends TunerResourceBasic { * * @param handles the handle collection to be added. */ - public void addExclusiveGroupMemberFeHandles(Collection<Integer> handles) { + public void addExclusiveGroupMemberFeHandles(Collection<Long> handles) { mExclusiveGroupMemberHandles.addAll(handles); } @@ -85,7 +85,7 @@ public final class FrontendResource extends TunerResourceBasic { * * @param id the id to be removed. */ - public void removeExclusiveGroupMemberFeId(int handle) { + public void removeExclusiveGroupMemberFeId(long handle) { mExclusiveGroupMemberHandles.remove(handle); } @@ -104,7 +104,7 @@ public final class FrontendResource extends TunerResourceBasic { @Type private int mType; private int mExclusiveGroupId; - Builder(int handle) { + Builder(long handle) { super(handle); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java index 41cacea5f09e..ab283713b15a 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java @@ -37,8 +37,7 @@ public final class LnbResource extends TunerResourceBasic { * Builder class for {@link LnbResource}. */ public static class Builder extends TunerResourceBasic.Builder { - - Builder(int handle) { + Builder(long handle) { super(handle); } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java index 07853fc69055..d2ff8fa7c2e8 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java @@ -28,7 +28,7 @@ public class TunerResourceBasic { * Handle of the current resource. Should not be changed and should be aligned with the driver * level implementation. */ - final int mHandle; + final long mHandle; /** * If the current resource is in use. @@ -44,7 +44,7 @@ public class TunerResourceBasic { this.mHandle = builder.mHandle; } - public int getHandle() { + public long getHandle() { return mHandle; } @@ -78,9 +78,9 @@ public class TunerResourceBasic { * Builder class for {@link TunerResourceBasic}. */ public static class Builder { - private final int mHandle; + private final long mHandle; - Builder(int handle) { + Builder(long handle) { this.mHandle = handle; } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 9229f7f016bc..c5b6bbf30ae1 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -78,12 +78,18 @@ public class TunerResourceManagerService extends SystemService implements IBinde private static final int INVALID_FE_COUNT = -1; + private static final int RESOURCE_ID_SHIFT = 24; + private static final int RESOURCE_TYPE_SHIFT = 56; + private static final long RESOURCE_COUNT_MASK = 0xffffff; + private static final long RESOURCE_ID_MASK = 0xffffffff; + private static final long RESOURCE_TYPE_MASK = 0xff; + // Map of the registered client profiles private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>(); private int mNextUnusedClientId = 0; // Map of the current available frontend resources - private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>(); + private Map<Long, FrontendResource> mFrontendResources = new HashMap<>(); // SparseIntArray of the max usable number for each frontend resource type private SparseIntArray mFrontendMaxUsableNums = new SparseIntArray(); // SparseIntArray of the currently used number for each frontend resource type @@ -93,15 +99,15 @@ public class TunerResourceManagerService extends SystemService implements IBinde // Backups for the frontend resource maps for enabling testing with custom resource maps // such as TunerTest.testHasUnusedFrontend1() - private Map<Integer, FrontendResource> mFrontendResourcesBackup = new HashMap<>(); + private Map<Long, FrontendResource> mFrontendResourcesBackup = new HashMap<>(); private SparseIntArray mFrontendMaxUsableNumsBackup = new SparseIntArray(); private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray(); private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray(); // Map of the current available demux resources - private Map<Integer, DemuxResource> mDemuxResources = new HashMap<>(); + private Map<Long, DemuxResource> mDemuxResources = new HashMap<>(); // Map of the current available lnb resources - private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); + private Map<Long, LnbResource> mLnbResources = new HashMap<>(); // Map of the current available Cas resources private Map<Integer, CasResource> mCasResources = new HashMap<>(); // Map of the current available CiCam resources @@ -266,7 +272,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public void setLnbInfoList(int[] lnbHandles) throws RemoteException { + public void setLnbInfoList(long[] lnbHandles) throws RemoteException { enforceTrmAccessPermission("setLnbInfoList"); if (lnbHandles == null) { throw new RemoteException("Lnb handle list can't be null"); @@ -277,8 +283,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public boolean requestFrontend(@NonNull TunerFrontendRequest request, - @NonNull int[] frontendHandle) { + public boolean requestFrontend( + @NonNull TunerFrontendRequest request, @NonNull long[] frontendHandle) { enforceTunerAccessPermission("requestFrontend"); enforceTrmAccessPermission("requestFrontend"); if (frontendHandle == null) { @@ -350,8 +356,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public boolean requestDemux(@NonNull TunerDemuxRequest request, - @NonNull int[] demuxHandle) throws RemoteException { + public boolean requestDemux(@NonNull TunerDemuxRequest request, @NonNull long[] demuxHandle) + throws RemoteException { enforceTunerAccessPermission("requestDemux"); enforceTrmAccessPermission("requestDemux"); if (demuxHandle == null) { @@ -362,7 +368,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde @Override public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, - @NonNull int[] descramblerHandle) throws RemoteException { + @NonNull long[] descramblerHandle) throws RemoteException { enforceDescramblerAccessPermission("requestDescrambler"); enforceTrmAccessPermission("requestDescrambler"); if (descramblerHandle == null) { @@ -379,7 +385,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde @Override public boolean requestCasSession(@NonNull CasSessionRequest request, - @NonNull int[] casSessionHandle) throws RemoteException { + @NonNull long[] casSessionHandle) throws RemoteException { enforceTrmAccessPermission("requestCasSession"); if (casSessionHandle == null) { throw new RemoteException("casSessionHandle can't be null"); @@ -388,8 +394,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public boolean requestCiCam(@NonNull TunerCiCamRequest request, - @NonNull int[] ciCamHandle) throws RemoteException { + public boolean requestCiCam(@NonNull TunerCiCamRequest request, @NonNull long[] ciCamHandle) + throws RemoteException { enforceTrmAccessPermission("requestCiCam"); if (ciCamHandle == null) { throw new RemoteException("ciCamHandle can't be null"); @@ -398,7 +404,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull int[] lnbHandle) + public boolean requestLnb(@NonNull TunerLnbRequest request, @NonNull long[] lnbHandle) throws RemoteException { enforceTunerAccessPermission("requestLnb"); enforceTrmAccessPermission("requestLnb"); @@ -409,14 +415,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public void releaseFrontend(int frontendHandle, int clientId) throws RemoteException { + public void releaseFrontend(long frontendHandle, int clientId) throws RemoteException { enforceTunerAccessPermission("releaseFrontend"); enforceTrmAccessPermission("releaseFrontend"); releaseFrontendInternal(frontendHandle, clientId); } @Override - public void releaseDemux(int demuxHandle, int clientId) throws RemoteException { + public void releaseDemux(long demuxHandle, int clientId) throws RemoteException { enforceTunerAccessPermission("releaseDemux"); enforceTrmAccessPermission("releaseDemux"); if (DEBUG) { @@ -447,7 +453,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public void releaseDescrambler(int descramblerHandle, int clientId) { + public void releaseDescrambler(long descramblerHandle, int clientId) { enforceTunerAccessPermission("releaseDescrambler"); enforceTrmAccessPermission("releaseDescrambler"); if (DEBUG) { @@ -456,7 +462,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException { + public void releaseCasSession(long casSessionHandle, int clientId) throws RemoteException { enforceTrmAccessPermission("releaseCasSession"); if (!validateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) { @@ -480,7 +486,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public void releaseCiCam(int ciCamHandle, int clientId) throws RemoteException { + public void releaseCiCam(long ciCamHandle, int clientId) throws RemoteException { enforceTrmAccessPermission("releaseCiCam"); if (!validateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, ciCamHandle)) { @@ -508,7 +514,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override - public void releaseLnb(int lnbHandle, int clientId) throws RemoteException { + public void releaseLnb(long lnbHandle, int clientId) throws RemoteException { enforceTunerAccessPermission("releaseLnb"); enforceTrmAccessPermission("releaseLnb"); if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) { @@ -812,7 +818,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde // A set to record the frontends pending on updating. Ids will be removed // from this set once its updating finished. Any frontend left in this set when all // the updates are done will be removed from mFrontendResources. - Set<Integer> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet()); + Set<Long> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet()); // Update frontendResources map and other mappings accordingly for (int i = 0; i < infos.length; i++) { @@ -831,7 +837,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } - for (int removingHandle : updatingFrontendHandles) { + for (long removingHandle : updatingFrontendHandles) { // update the exclusive group id member list removeFrontendResource(removingHandle); } @@ -849,7 +855,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde // A set to record the demuxes pending on updating. Ids will be removed // from this set once its updating finished. Any demux left in this set when all // the updates are done will be removed from mDemuxResources. - Set<Integer> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet()); + Set<Long> updatingDemuxHandles = new HashSet<>(getDemuxResources().keySet()); // Update demuxResources map and other mappings accordingly for (int i = 0; i < infos.length; i++) { @@ -867,13 +873,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } - for (int removingHandle : updatingDemuxHandles) { + for (long removingHandle : updatingDemuxHandles) { // update the exclusive group id member list removeDemuxResource(removingHandle); } } @VisibleForTesting - protected void setLnbInfoListInternal(int[] lnbHandles) { + protected void setLnbInfoListInternal(long[] lnbHandles) { if (DEBUG) { for (int i = 0; i < lnbHandles.length; i++) { Slog.d(TAG, "updateLnbInfo(lnbHanle=" + lnbHandles[i] + ")"); @@ -883,7 +889,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde // A set to record the Lnbs pending on updating. Handles will be removed // from this set once its updating finished. Any lnb left in this set when all // the updates are done will be removed from mLnbResources. - Set<Integer> updatingLnbHandles = new HashSet<>(getLnbResources().keySet()); + Set<Long> updatingLnbHandles = new HashSet<>(getLnbResources().keySet()); // Update lnbResources map and other mappings accordingly for (int i = 0; i < lnbHandles.length; i++) { @@ -899,7 +905,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } - for (int removingHandle : updatingLnbHandles) { + for (long removingHandle : updatingLnbHandles) { removeLnbResource(removingHandle); } } @@ -933,12 +939,12 @@ public class TunerResourceManagerService extends SystemService implements IBinde return; } // Add the new Cas Resource. - int casSessionHandle = generateResourceHandle( + long casSessionHandle = generateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSystemId); cas = new CasResource.Builder(casSessionHandle, casSystemId) .maxSessionNum(maxSessionNum) .build(); - int ciCamHandle = generateResourceHandle( + long ciCamHandle = generateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, casSystemId); ciCam = new CiCamResource.Builder(ciCamHandle, casSystemId) .maxSessionNum(maxSessionNum) @@ -948,7 +954,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) { + protected boolean requestFrontendInternal(TunerFrontendRequest request, long[] frontendHandle) { if (DEBUG) { Slog.d(TAG, "requestFrontend(request=" + request + ")"); } @@ -977,7 +983,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde protected boolean claimFrontend( TunerFrontendRequest request, - int[] frontendHandle, + long[] frontendHandle, int[] reclaimOwnerId ) { frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; @@ -1032,7 +1038,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde // currently in primary use (and simply blocked due to exclusive group) ClientProfile targetOwnerProfile = getClientProfile(fr.getOwnerClientId()); - int primaryFeId = targetOwnerProfile.getPrimaryFrontend(); + long primaryFeId = targetOwnerProfile.getPrimaryFrontend(); FrontendResource primaryFe = getFrontendResource(primaryFeId); if (fr.getType() != primaryFe.getType() && isFrontendMaxNumUseReached(fr.getType())) { @@ -1081,7 +1087,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde getClientProfile(shareeFeClientId).stopSharingFrontend(selfClientId); getClientProfile(selfClientId).releaseFrontend(); } - for (int feId : getClientProfile(targetClientId).getInUseFrontendHandles()) { + for (long feId : getClientProfile(targetClientId).getInUseFrontendHandles()) { getClientProfile(selfClientId).useFrontend(feId); } getClientProfile(selfClientId).setShareeFeClientId(targetClientId); @@ -1096,14 +1102,14 @@ public class TunerResourceManagerService extends SystemService implements IBinde currentOwnerProfile.stopSharingFrontend(newOwnerId); newOwnerProfile.setShareeFeClientId(ClientProfile.INVALID_RESOURCE_ID); currentOwnerProfile.setShareeFeClientId(newOwnerId); - for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) { + for (long inUseHandle : newOwnerProfile.getInUseFrontendHandles()) { getFrontendResource(inUseHandle).setOwner(newOwnerId); } // change the primary frontend newOwnerProfile.setPrimaryFrontend(currentOwnerProfile.getPrimaryFrontend()); currentOwnerProfile.setPrimaryFrontend(TunerResourceManager.INVALID_RESOURCE_HANDLE); // double check there is no other resources tied to the previous owner - for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) { + for (long inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) { int ownerId = getFrontendResource(inUseHandle).getOwnerClientId(); if (ownerId != newOwnerId) { Slog.e(TAG, "something is wrong in transferFeOwner:" + inUseHandle @@ -1135,8 +1141,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde ClientProfile currentOwnerProfile = getClientProfile(currentOwnerId); ClientProfile newOwnerProfile = getClientProfile(newOwnerId); - Set<Integer> inUseLnbHandles = new HashSet<>(); - for (Integer lnbHandle : currentOwnerProfile.getInUseLnbHandles()) { + Set<Long> inUseLnbHandles = new HashSet<>(); + for (Long lnbHandle : currentOwnerProfile.getInUseLnbHandles()) { // link lnb handle to the new profile newOwnerProfile.useLnb(lnbHandle); @@ -1148,7 +1154,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } // unlink lnb handles from the original owner - for (Integer lnbHandle : inUseLnbHandles) { + for (Long lnbHandle : inUseLnbHandles) { currentOwnerProfile.releaseLnb(lnbHandle); } @@ -1171,7 +1177,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) + protected boolean requestLnbInternal(TunerLnbRequest request, long[] lnbHandle) throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestLnb(request=" + request + ")"); @@ -1199,7 +1205,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return true; } - protected boolean claimLnb(TunerLnbRequest request, int[] lnbHandle, int[] reclaimOwnerId) + protected boolean claimLnb(TunerLnbRequest request, long[] lnbHandle, int[] reclaimOwnerId) throws RemoteException { lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; reclaimOwnerId[0] = INVALID_CLIENT_ID; @@ -1256,7 +1262,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) + protected boolean requestCasSessionInternal(CasSessionRequest request, long[] casSessionHandle) throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestCasSession(request=" + request + ")"); @@ -1284,7 +1290,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return true; } - protected boolean claimCasSession(CasSessionRequest request, int[] casSessionHandle, + protected boolean claimCasSession(CasSessionRequest request, long[] casSessionHandle, int[] reclaimOwnerId) throws RemoteException { casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; reclaimOwnerId[0] = INVALID_CLIENT_ID; @@ -1296,7 +1302,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde CasResource cas = getCasResource(request.casSystemId); // Unregistered Cas System is treated as having unlimited sessions. if (cas == null) { - int resourceHandle = generateResourceHandle( + long resourceHandle = generateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, request.clientId); cas = new CasResource.Builder(resourceHandle, request.casSystemId) .maxSessionNum(Integer.MAX_VALUE) @@ -1341,7 +1347,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected boolean requestCiCamInternal(TunerCiCamRequest request, int[] ciCamHandle) + protected boolean requestCiCamInternal(TunerCiCamRequest request, long[] ciCamHandle) throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestCiCamInternal(TunerCiCamRequest=" + request + ")"); @@ -1369,7 +1375,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return true; } - protected boolean claimCiCam(TunerCiCamRequest request, int[] ciCamHandle, + protected boolean claimCiCam(TunerCiCamRequest request, long[] ciCamHandle, int[] reclaimOwnerId) throws RemoteException { ciCamHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; reclaimOwnerId[0] = INVALID_CLIENT_ID; @@ -1381,7 +1387,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde CiCamResource ciCam = getCiCamResource(request.ciCamId); // Unregistered CiCam is treated as having unlimited sessions. if (ciCam == null) { - int resourceHandle = generateResourceHandle( + long resourceHandle = generateResourceHandle( TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND_CICAM, request.ciCamId); ciCam = new CiCamResource.Builder(resourceHandle, request.ciCamId) .maxSessionNum(Integer.MAX_VALUE) @@ -1454,7 +1460,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @VisibleForTesting - protected void releaseFrontendInternal(int frontendHandle, int clientId) + protected void releaseFrontendInternal(long frontendHandle, int clientId) throws RemoteException { if (DEBUG) { Slog.d(TAG, "releaseFrontend(id=" + frontendHandle + ", clientId=" + clientId + " )"); @@ -1475,7 +1481,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } - private Set<Integer> unclaimFrontend(int frontendHandle, int clientId) throws RemoteException { + private Set<Integer> unclaimFrontend(long frontendHandle, int clientId) throws RemoteException { Set<Integer> reclaimedResourceOwnerIds = null; synchronized (mLock) { if (!checkClientExists(clientId)) { @@ -1533,7 +1539,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde @VisibleForTesting public boolean requestDemuxInternal(@NonNull TunerDemuxRequest request, - @NonNull int[] demuxHandle) throws RemoteException { + @NonNull long[] demuxHandle) throws RemoteException { if (DEBUG) { Slog.d(TAG, "requestDemux(request=" + request + ")"); } @@ -1560,7 +1566,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde return true; } - protected boolean claimDemux(TunerDemuxRequest request, int[] demuxHandle, int[] reclaimOwnerId) + protected boolean claimDemux( + TunerDemuxRequest request, long[] demuxHandle, int[] reclaimOwnerId) throws RemoteException { demuxHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; reclaimOwnerId[0] = INVALID_CLIENT_ID; @@ -1676,7 +1683,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde @VisibleForTesting protected boolean requestDescramblerInternal( - TunerDescramblerRequest request, int[] descramblerHandle) { + TunerDescramblerRequest request, long[] descramblerHandle) { if (DEBUG) { Slog.d(TAG, "requestDescrambler(request=" + request + ")"); } @@ -2008,20 +2015,20 @@ public class TunerResourceManagerService extends SystemService implements IBinde return false; } - private void updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { + private void updateFrontendClientMappingOnNewGrant(long grantingHandle, int ownerClientId) { FrontendResource grantingFrontend = getFrontendResource(grantingHandle); ClientProfile ownerProfile = getClientProfile(ownerClientId); grantingFrontend.setOwner(ownerClientId); increFrontendNum(mFrontendUsedNums, grantingFrontend.getType()); ownerProfile.useFrontend(grantingHandle); - for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) { + for (long exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) { getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId); ownerProfile.useFrontend(exclusiveGroupMember); } ownerProfile.setPrimaryFrontend(grantingHandle); } - private void updateDemuxClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { + private void updateDemuxClientMappingOnNewGrant(long grantingHandle, int ownerClientId) { DemuxResource grantingDemux = getDemuxResource(grantingHandle); if (grantingDemux != null) { ClientProfile ownerProfile = getClientProfile(ownerClientId); @@ -2036,7 +2043,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde ownerProfile.releaseDemux(releasingDemux.getHandle()); } - private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { + private void updateLnbClientMappingOnNewGrant(long grantingHandle, int ownerClientId) { LnbResource grantingLnb = getLnbResource(grantingHandle); ClientProfile ownerProfile = getClientProfile(ownerClientId); grantingLnb.setOwner(ownerClientId); @@ -2120,23 +2127,23 @@ public class TunerResourceManagerService extends SystemService implements IBinde @VisibleForTesting @Nullable - protected FrontendResource getFrontendResource(int frontendHandle) { + protected FrontendResource getFrontendResource(long frontendHandle) { return mFrontendResources.get(frontendHandle); } @VisibleForTesting - protected Map<Integer, FrontendResource> getFrontendResources() { + protected Map<Long, FrontendResource> getFrontendResources() { return mFrontendResources; } @VisibleForTesting @Nullable - protected DemuxResource getDemuxResource(int demuxHandle) { + protected DemuxResource getDemuxResource(long demuxHandle) { return mDemuxResources.get(demuxHandle); } @VisibleForTesting - protected Map<Integer, DemuxResource> getDemuxResources() { + protected Map<Long, DemuxResource> getDemuxResources() { return mDemuxResources; } @@ -2195,8 +2202,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } - private void replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer, - FrontendResource> dstMap) { + private void replaceFeResourceMap( + Map<Long, FrontendResource> srcMap, Map<Long, FrontendResource> dstMap) { if (dstMap != null) { dstMap.clear(); if (srcMap != null && srcMap.size() > 0) { @@ -2249,7 +2256,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) { newFe.addExclusiveGroupMemberFeHandle(fe.getHandle()); newFe.addExclusiveGroupMemberFeHandles(fe.getExclusiveGroupMemberFeHandles()); - for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { + for (long excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { getFrontendResource(excGroupmemberFeHandle) .addExclusiveGroupMemberFeHandle(newFe.getHandle()); } @@ -2267,7 +2274,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde mDemuxResources.put(newDemux.getHandle(), newDemux); } - private void removeFrontendResource(int removingHandle) { + private void removeFrontendResource(long removingHandle) { FrontendResource fe = getFrontendResource(removingHandle); if (fe == null) { return; @@ -2279,7 +2286,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde } clearFrontendAndClientMapping(ownerClient); } - for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { + for (long excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) { getFrontendResource(excGroupmemberFeHandle) .removeExclusiveGroupMemberFeId(fe.getHandle()); } @@ -2287,7 +2294,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde mFrontendResources.remove(removingHandle); } - private void removeDemuxResource(int removingHandle) { + private void removeDemuxResource(long removingHandle) { DemuxResource demux = getDemuxResource(removingHandle); if (demux == null) { return; @@ -2300,12 +2307,12 @@ public class TunerResourceManagerService extends SystemService implements IBinde @VisibleForTesting @Nullable - protected LnbResource getLnbResource(int lnbHandle) { + protected LnbResource getLnbResource(long lnbHandle) { return mLnbResources.get(lnbHandle); } @VisibleForTesting - protected Map<Integer, LnbResource> getLnbResources() { + protected Map<Long, LnbResource> getLnbResources() { return mLnbResources; } @@ -2314,7 +2321,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde mLnbResources.put(newLnb.getHandle(), newLnb); } - private void removeLnbResource(int removingHandle) { + private void removeLnbResource(long removingHandle) { LnbResource lnb = getLnbResource(removingHandle); if (lnb == null) { return; @@ -2416,7 +2423,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (profile == null) { return; } - for (Integer feId : profile.getInUseFrontendHandles()) { + for (Long feId : profile.getInUseFrontendHandles()) { FrontendResource fe = getFrontendResource(feId); int ownerClientId = fe.getOwnerClientId(); if (ownerClientId == profile.getId()) { @@ -2427,10 +2434,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde if (ownerClientProfile != null) { ownerClientProfile.stopSharingFrontend(profile.getId()); } - } - int primaryFeId = profile.getPrimaryFrontend(); + long primaryFeId = profile.getPrimaryFrontend(); if (primaryFeId != TunerResourceManager.INVALID_RESOURCE_HANDLE) { FrontendResource primaryFe = getFrontendResource(primaryFeId); if (primaryFe != null) { @@ -2448,7 +2454,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde return; } // Clear Lnb - for (Integer lnbHandle : profile.getInUseLnbHandles()) { + for (Long lnbHandle : profile.getInUseLnbHandles()) { getLnbResource(lnbHandle).removeOwner(); } // Clear Cas @@ -2460,7 +2466,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde getCiCamResource(profile.getInUseCiCamId()).removeOwner(profile.getId()); } // Clear Demux - for (Integer demuxHandle : profile.getInUseDemuxHandles()) { + for (Long demuxHandle : profile.getInUseDemuxHandles()) { getDemuxResource(demuxHandle).removeOwner(); } // Clear Frontend @@ -2473,24 +2479,31 @@ public class TunerResourceManagerService extends SystemService implements IBinde return mClientProfiles.keySet().contains(clientId); } - private int generateResourceHandle( + /** + * Generate resource handle for resourceType and resourceId + * Resource Handle Allotment : 64 bits (long) + * 8 bits - resourceType + * 32 bits - resourceId + * 24 bits - resourceRequestCount + */ + private long generateResourceHandle( @TunerResourceManager.TunerResourceType int resourceType, int resourceId) { - return (resourceType & 0x000000ff) << 24 - | (resourceId << 16) - | (mResourceRequestCount++ & 0xffff); + return (resourceType & RESOURCE_TYPE_MASK) << RESOURCE_TYPE_SHIFT + | (resourceId & RESOURCE_ID_MASK) << RESOURCE_ID_SHIFT + | (mResourceRequestCount++ & RESOURCE_COUNT_MASK); } @VisibleForTesting - protected int getResourceIdFromHandle(int resourceHandle) { + protected int getResourceIdFromHandle(long resourceHandle) { if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) { - return resourceHandle; + return (int) resourceHandle; } - return (resourceHandle & 0x00ff0000) >> 16; + return (int) ((resourceHandle >> RESOURCE_ID_SHIFT) & RESOURCE_ID_MASK); } - private boolean validateResourceHandle(int resourceType, int resourceHandle) { + private boolean validateResourceHandle(int resourceType, long resourceHandle) { if (resourceHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE - || ((resourceHandle & 0xff000000) >> 24) != resourceType) { + || ((resourceHandle >> RESOURCE_TYPE_SHIFT) & RESOURCE_TYPE_MASK) != resourceType) { return false; } return true; diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java index 5f704a002a33..6f1e15b5033f 100644 --- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -29,7 +29,6 @@ import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.IpSecTransformState; import android.net.Network; -import android.net.vcn.Flags; import android.net.vcn.VcnManager; import android.os.Handler; import android.os.HandlerExecutor; @@ -233,7 +232,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { @VisibleForTesting(visibility = Visibility.PRIVATE) static int getMaxSeqNumIncreasePerSecond(@Nullable PersistableBundleWrapper carrierConfig) { int maxSeqNumIncrease = MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED; - if (Flags.handleSeqNumLeap() && carrierConfig != null) { + if (carrierConfig != null) { maxSeqNumIncrease = carrierConfig.getInt( VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY, @@ -287,10 +286,8 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { // with the new interval mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); - if (Flags.handleSeqNumLeap()) { - mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); - mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig); - } + mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig); if (canStart() != isStarted()) { if (canStart()) { @@ -438,13 +435,10 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { onValidationResultReceivedInternal(true /* isFailed */); } - // In both "valid" or "unusual_seq_num_leap" cases, trigger network validation - if (Flags.validateNetworkOnIpsecLoss()) { - // Trigger re-validation of the underlying network; if it fails, the VCN will - // attempt to migrate away. - mConnectivityManager.reportNetworkConnectivity( - getNetwork(), false /* hasConnectivity */); - } + // In both "invalid" and "unusual_seq_num_leap" cases, trigger network validation. If + // validation fails, the VCN will attempt to migrate away. + mConnectivityManager.reportNetworkConnectivity( + getNetwork(), false /* hasConnectivity */); } } @@ -474,8 +468,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { boolean isUnusualSeqNumLeap = false; // Handle sequence number leap - if (Flags.handleSeqNumLeap() - && maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) { + if (maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) { final long timeDiffMillis = newState.getTimestampMillis() - oldState.getTimestampMillis(); final long maxSeqNumIncrease = timeDiffMillis * maxSeqNumIncreasePerSecond / 1000; @@ -506,7 +499,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { + " actualPktCntDiff: " + actualPktCntDiff); - if (Flags.handleSeqNumLeap() && expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) { + if (expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) { // The sample size is too small to ensure a reliable detection result return PacketLossCalculationResult.invalid(); } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java index 78e06d46c74c..c852fb4e170f 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java @@ -25,7 +25,6 @@ import android.net.IpSecTransform; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; -import android.net.vcn.Flags; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.Handler; @@ -297,10 +296,8 @@ public class UnderlyingNetworkEvaluator { updatePriorityClass( underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); - if (Flags.evaluateIpsecLossOnLpNcChange()) { - for (NetworkMetricMonitor monitor : mMetricMonitors) { - monitor.onLinkPropertiesOrCapabilitiesChanged(); - } + for (NetworkMetricMonitor monitor : mMetricMonitors) { + monitor.onLinkPropertiesOrCapabilitiesChanged(); } } @@ -316,10 +313,8 @@ public class UnderlyingNetworkEvaluator { updatePriorityClass( underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); - if (Flags.evaluateIpsecLossOnLpNcChange()) { - for (NetworkMetricMonitor monitor : mMetricMonitors) { - monitor.onLinkPropertiesOrCapabilitiesChanged(); - } + for (NetworkMetricMonitor monitor : mMetricMonitors) { + monitor.onLinkPropertiesOrCapabilitiesChanged(); } } diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java index 006a5bb1d1d0..a235ba154579 100644 --- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java @@ -148,8 +148,8 @@ public final class HapticFeedbackVibrationProvider { case HapticFeedbackConstants.SCROLL_TICK: case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: case HapticFeedbackConstants.SCROLL_LIMIT: - attrs = hapticFeedbackInputSourceCustomizationEnabled() ? TOUCH_VIBRATION_ATTRIBUTES - : HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES; + // TODO(b/372820923): use touch attributes by default. + attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES; break; case HapticFeedbackConstants.KEYBOARD_TAP: case HapticFeedbackConstants.KEYBOARD_RELEASE: @@ -176,14 +176,15 @@ public final class HapticFeedbackVibrationProvider { int inputSource, @HapticFeedbackConstants.Flags int flags, @HapticFeedbackConstants.PrivateFlags int privFlags) { - if (hapticFeedbackInputSourceCustomizationEnabled() - && inputSource == InputDevice.SOURCE_ROTARY_ENCODER) { + if (hapticFeedbackInputSourceCustomizationEnabled()) { switch (effectId) { case HapticFeedbackConstants.SCROLL_TICK, HapticFeedbackConstants.SCROLL_ITEM_FOCUS, HapticFeedbackConstants.SCROLL_LIMIT -> { - return getVibrationAttributesWithFlags(HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES, - effectId, flags); + VibrationAttributes attrs = inputSource == InputDevice.SOURCE_ROTARY_ENCODER + ? HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES + : TOUCH_VIBRATION_ATTRIBUTES; + return getVibrationAttributesWithFlags(attrs, effectId, flags); } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index a44eb48d7836..460de01a7d1d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -285,9 +285,6 @@ import android.app.servertransaction.StopActivityItem; import android.app.servertransaction.TopResumedActivityChangeItem; import android.app.servertransaction.TransferSplashScreenViewStateItem; import android.app.usage.UsageEvents.Event; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; -import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -467,11 +464,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // finished destroying itself. private static final int DESTROY_TIMEOUT = 10 * 1000; - @ChangeId - @Overridable - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) - static final long UNIVERSAL_RESIZABLE_BY_DEFAULT = 357141415; - final ActivityTaskManagerService mAtmService; final ActivityCallerState mCallerState; @NonNull @@ -3192,7 +3184,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && mDisplayContent != null && mDisplayContent.getConfiguration() .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP && mDisplayContent.getIgnoreOrientationRequest() - && info.isChangeEnabled(UNIVERSAL_RESIZABLE_BY_DEFAULT); + && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT); if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) { return false; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 87fa62ac0e3b..5b5bb88cac98 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -86,6 +86,7 @@ import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENS import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK; import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST; import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg; import android.annotation.IntDef; @@ -2786,10 +2787,22 @@ class ActivityStarter { } } - if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 - && ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 || mSourceRecord == null)) { - // ignore the flag if there is no the sourceRecord or without new_task flag - mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { + final boolean hasNewTaskFlag = (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0; + if (!hasNewTaskFlag || mSourceRecord == null) { + // ignore the flag if there is no the sourceRecord or without new_task flag + Slog.w(TAG_WM, !hasNewTaskFlag + ? "Launch adjacent ignored due to missing NEW_TASK" + : "Launch adjacent ignored due to missing source activity"); + mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + } + // Ensure that the source task or its parents has not disabled launch-adjacent + if (mSourceRecord != null && mSourceRecord.getTask() != null && + mSourceRecord.getTask().isLaunchAdjacentDisabled()) { + Slog.w(TAG_WM, "Launch adjacent blocked by source task or ancestor"); + mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 3d6b64b2e536..3560565ce9cd 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -589,7 +589,7 @@ public abstract class ActivityTaskManagerInternal { * sensitive environment. */ public abstract TaskSnapshot getTaskSnapshotBlocking(int taskId, - boolean isLowResolution); + boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage); /** Returns true if uid is considered foreground for activity start purposes. */ public abstract boolean isUidForeground(int uid); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3dfc8f4e5bf9..4db478a13c92 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3946,6 +3946,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + private TaskSnapshot getTaskSnapshotInner(int taskId, boolean isLowResolution, + @TaskSnapshot.ReferenceFlags int usage) { + final Task task; + synchronized (mGlobalLock) { + task = mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS); + if (task == null) { + Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found"); + return null; + } + // Try to load snapshot from cache first, and add reference if the snapshot is in cache. + final TaskSnapshot snapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId, + task.mUserId, false /* restoreFromDisk */, isLowResolution); + if (snapshot != null) { + snapshot.addReference(usage); + return snapshot; + } + } + // Don't call this while holding the lock as this operation might hit the disk. + return mWindowManager.mTaskSnapshotController.getSnapshot(taskId, + task.mUserId, true /* restoreFromDisk */, isLowResolution); + } + @Override public TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution) { mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()"); @@ -7274,8 +7296,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public TaskSnapshot getTaskSnapshotBlocking( - int taskId, boolean isLowResolution) { - return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution); + int taskId, boolean isLowResolution, @TaskSnapshot.ReferenceFlags int usage) { + return ActivityTaskManagerService.this.getTaskSnapshotInner( + taskId, isLowResolution, usage); } @Override diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 515f148ac2ff..a380ba1a6f11 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -558,7 +558,7 @@ public class BackgroundActivityStartController { .append(mBalAllowedByPiCreatorWithHardening); sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller); sb.append("; callerStartMode: ").append(balStartModeToString( - mCheckedOptions.getPendingIntentBackgroundActivityStartMode())); + mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode())); sb.append("; hasRealCaller: ").append(hasRealCaller()); sb.append("; isCallForResult: ").append(mIsCallForResult); sb.append("; isPendingIntent: ").append(isPendingIntent()); @@ -585,7 +585,7 @@ public class BackgroundActivityStartController { sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender); sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller); sb.append("; realCallerStartMode: ").append(balStartModeToString( - mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode())); + mCheckedOptions.getPendingIntentBackgroundActivityStartMode())); } // features sb.append("; balImproveRealCallerVisibilityCheck: ") @@ -792,7 +792,8 @@ public class BackgroundActivityStartController { // Allowed before V by creator if (state.mBalAllowedByPiCreator.allowsBackgroundActivityStarts()) { Slog.wtf(TAG, "With Android 15 BAL hardening this activity start may be blocked" - + " if the PI creator upgrades target_sdk to 35+! " + + " if the PI creator upgrades target_sdk to 35+!" + + " goo.gle/android-bal" + " (missing opt in by PI creator)!" + state); return allowBasedOnCaller(state); } @@ -802,6 +803,7 @@ public class BackgroundActivityStartController { if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()) { Slog.wtf(TAG, "With Android 14 BAL hardening this activity start will be blocked" + " if the PI sender upgrades target_sdk to 34+! " + + " goo.gle/android-bal" + " (missing opt in by PI sender)!" + state); return allowBasedOnRealCaller(state); } @@ -829,7 +831,8 @@ public class BackgroundActivityStartController { } private BalVerdict abortLaunch(BalState state) { - Slog.wtf(TAG, "Background activity launch blocked! " + state); + Slog.wtf(TAG, "Background activity launch blocked! goo.gle/android-bal " + + state); if (balShowToastsBlocked() && (state.mResultForCaller.allows() || state.mResultForRealCaller.allows())) { // only show a toast if either caller or real caller could launch if they opted in @@ -1044,6 +1047,24 @@ public class BackgroundActivityStartController { "realCallingUid has BAL permission."); } + // don't abort if the realCallingUid has SYSTEM_ALERT_WINDOW permission + Slog.i(TAG, "hasSystemAlertWindowPermission(" + state.mRealCallingUid + ", " + + state.mRealCallingPid + ", " + state.mRealCallingPackage + ") " + + balStartModeToString( + state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode())); + if (state.mCheckedOptions.getPendingIntentBackgroundActivityStartMode() + == MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS + && mService.hasSystemAlertWindowPermission(state.mRealCallingUid, + state.mRealCallingPid, state.mRealCallingPackage)) { + Slog.w( + TAG, + "Background activity start for " + + state.mRealCallingPackage + + " allowed because SYSTEM_ALERT_WINDOW permission is granted."); + return new BalVerdict(BAL_ALLOW_SAW_PERMISSION, + /*background*/ true, "SYSTEM_ALERT_WINDOW permission is granted"); + } + // if the realCallingUid is a persistent system process, abort if the IntentSender // wasn't allowed to start an activity if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts() diff --git a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java index 3dc035e62e58..cbe3d79f10fc 100644 --- a/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java +++ b/services/core/java/com/android/server/wm/DeferredDisplayUpdater.java @@ -401,6 +401,7 @@ class DeferredDisplayUpdater { || !Objects.equals(first.deviceProductInfo, second.deviceProductInfo) || first.modeId != second.modeId || first.renderFrameRate != second.renderFrameRate + || first.hasArrSupport != second.hasArrSupport || first.defaultModeId != second.defaultModeId || first.userPreferredModeId != second.userPreferredModeId || !Arrays.equals(first.supportedModes, second.supportedModes) diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 98919d9fd617..a4e4deb9ed7d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -503,6 +503,11 @@ class Task extends TaskFragment { boolean mIsTrimmableFromRecents; /** + * Sets whether the launch-adjacent flag is respected or not for this task or its child tasks. + */ + private boolean mLaunchAdjacentDisabled; + + /** * Bounds offset should be applied when calculating compatible configuration for apps targeting * SDK level 34 or before. */ @@ -3802,6 +3807,9 @@ class Task extends TaskFragment { pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)"); pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents); + if (mLaunchAdjacentDisabled) { + pw.println(prefix + "mLaunchAdjacentDisabled=true"); + } } @Override @@ -5734,6 +5742,12 @@ class Task extends TaskFragment { } private boolean canMoveTaskToBack(Task task) { + // Checks whether a task is a child of this task because it can be reparetned when + // transition is deferred. + if (task != this && task.getParent() != this) { + return false; + } + // In LockTask mode, moving a locked task to the back of the root task may expose unlocked // ones. Therefore we need to check if this operation is allowed. if (!mAtmService.getLockTaskController().canMoveTaskToBack(task)) { @@ -5803,7 +5817,7 @@ class Task extends TaskFragment { (deferred) -> { // Need to check again if deferred since the system might // be in a different state. - if (!isAttached() || (deferred && !canMoveTaskToBack(tr))) { + if (!tr.isAttached() || (deferred && !canMoveTaskToBack(tr))) { Slog.e(TAG, "Failed to move task to back after saying we could: " + tr.mTaskId); transition.abort(); @@ -6268,6 +6282,28 @@ class Task extends TaskFragment { } /** + * Sets this task and its children to disable respecting launch-adjacent. + */ + void setLaunchAdjacentDisabled(boolean disabled) { + mLaunchAdjacentDisabled = disabled; + } + + /** + * Returns whether this task or any of its ancestors have disabled respecting the + * launch-adjacent flag. + */ + boolean isLaunchAdjacentDisabled() { + Task t = this; + while (t != null) { + if (t.mLaunchAdjacentDisabled) { + return true; + } + t = t.getParent().asTask(); + } + return false; + } + + /** * Return true if the activityInfo has the same requiredDisplayCategory as this task. */ boolean isSameRequiredDisplayCategory(@NonNull ActivityInfo info) { diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 5dd3bbce4e96..2c71c1a1f4f3 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1074,6 +1074,8 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { final Task launchRootTask = Task.fromWindowContainerToken(options.getLaunchRootTask()); // We only allow this for created by organizer tasks. if (launchRootTask != null && launchRootTask.mCreatedByOrganizer) { + Slog.i(TAG_WM, "Using launch root task from activity options: taskId=" + + launchRootTask.mTaskId); return launchRootTask; } } @@ -1081,19 +1083,25 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Use launch-adjacent-flag-root if launching with launch-adjacent flag. if ((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 && mLaunchAdjacentFlagRootTask != null) { + final Task launchAdjacentRootAdjacentTask = + mLaunchAdjacentFlagRootTask.getAdjacentTask(); if (sourceTask != null && (sourceTask == candidateTask || sourceTask.topRunningActivity() == null)) { // Do nothing when task that is getting opened is same as the source or when // the source is no-longer valid. Slog.w(TAG_WM, "Ignoring LAUNCH_ADJACENT because adjacent source is gone."); } else if (sourceTask != null - && mLaunchAdjacentFlagRootTask.getAdjacentTask() != null + && launchAdjacentRootAdjacentTask != null && (sourceTask == mLaunchAdjacentFlagRootTask || sourceTask.isDescendantOf(mLaunchAdjacentFlagRootTask))) { - // If the adjacent launch is coming from the same root, launch to - // adjacent root instead. - return mLaunchAdjacentFlagRootTask.getAdjacentTask(); + // If the adjacent launch is coming from the same root that was specified as the + // launch-adjacent task, so instead we launch to its adjacent root instead. + Slog.i(TAG_WM, "Using adjacent-to specified launch-adjacent task: taskId=" + + launchAdjacentRootAdjacentTask.mTaskId); + return launchAdjacentRootAdjacentTask; } else { + Slog.i(TAG_WM, "Using specified launch-adjacent task: taskId=" + + mLaunchAdjacentFlagRootTask.mTaskId); return mLaunchAdjacentFlagRootTask; } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 82c7a9350eca..166d74b132bd 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -69,6 +69,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; @@ -1450,6 +1451,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub task.setTrimmableFromRecents(hop.isTrimmableFromRecents()); break; } + case HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT: { + final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); + final Task task = container != null ? container.asTask() : null; + if (task == null || !task.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached container: " + + container); + break; + } + task.setLaunchAdjacentDisabled(hop.isLaunchAdjacentDisabled()); + break; + } case HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION: { if (mService.mBackNavigationController.restoreBackNavigation()) { effects |= TRANSACT_EFFECTS_LIFECYCLE; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7e450dd965d6..aca6f7235714 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -16815,6 +16815,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } EnforcingAdmin enforcingAdmin; + + // TODO(b/370472975): enable when we stop policy enforecer callback from blocking the main + // thread if (Flags.setPermissionGrantStateCoexistence()) { enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( admin, @@ -16840,6 +16843,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { callback.sendResult(null); return; } + + // TODO(b/266924257): decide how to handle the internal state if the package doesn't + // exist, or the permission isn't requested by the app, because we could end up with + // inconsistent state between the policy engine and package manager. Also a package + // might get removed or has it's permission updated after we've set the policy. + if (grantState == PERMISSION_GRANT_STATE_DEFAULT) { + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.PERMISSION_GRANT(packageName, permission), + enforcingAdmin, + caller.getUserId()); + } else { + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.PERMISSION_GRANT(packageName, permission), + enforcingAdmin, + new IntegerPolicyValue(grantState), + caller.getUserId()); + } + int newState = mInjector.binderWithCleanCallingIdentity(() -> + getPermissionGrantStateForUser( + packageName, permission, caller, caller.getUserId())); + if (newState == grantState) { + callback.sendResult(Bundle.EMPTY); + } else { + callback.sendResult(null); + } } else { Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDefaultDeviceOwner(caller) @@ -16862,9 +16890,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } synchronized (getLockObject()) { long ident = mInjector.binderClearCallingIdentity(); + boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId()) + >= android.os.Build.VERSION_CODES.Q; + try { - boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId()) - >= android.os.Build.VERSION_CODES.Q; if (!isPostQAdmin) { // Legacy admins assume that they cannot control pre-M apps if (getTargetSdk(packageName, caller.getUserId()) @@ -16877,47 +16906,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { callback.sendResult(null); return; } - } catch (SecurityException e) { - Slogf.e(LOG_TAG, "Could not set permission grant state", e); - callback.sendResult(null); - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } - } - } - // TODO(b/278710449): enable when we stop policy enforecer callback from blocking the main - // thread - if (false) { - // TODO(b/266924257): decide how to handle the internal state if the package doesn't - // exist, or the permission isn't requested by the app, because we could end up with - // inconsistent state between the policy engine and package manager. Also a package - // might get removed or has it's permission updated after we've set the policy. - if (grantState == PERMISSION_GRANT_STATE_DEFAULT) { - mDevicePolicyEngine.removeLocalPolicy( - PolicyDefinition.PERMISSION_GRANT(packageName, permission), - enforcingAdmin, - caller.getUserId()); - } else { - mDevicePolicyEngine.setLocalPolicy( - PolicyDefinition.PERMISSION_GRANT(packageName, permission), - enforcingAdmin, - new IntegerPolicyValue(grantState), - caller.getUserId()); - } - int newState = mInjector.binderWithCleanCallingIdentity(() -> - getPermissionGrantStateForUser( - packageName, permission, caller, caller.getUserId())); - if (newState == grantState) { - callback.sendResult(Bundle.EMPTY); - } else { - callback.sendResult(null); - } - } else { - synchronized (getLockObject()) { - long ident = mInjector.binderClearCallingIdentity(); - try { - boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId()) - >= android.os.Build.VERSION_CODES.Q; if (grantState == PERMISSION_GRANT_STATE_GRANTED || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) { @@ -16939,7 +16927,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } catch (SecurityException e) { Slogf.e(LOG_TAG, "Could not set permission grant state", e); - callback.sendResult(null); } finally { mInjector.binderRestoreCallingIdentity(ident); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7ea1dcd9392d..b9727f9f3970 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -159,7 +159,7 @@ import com.android.server.contentsuggestions.ContentSuggestionsManagerService; import com.android.server.contextualsearch.ContextualSearchManagerService; import com.android.server.coverage.CoverageService; import com.android.server.cpu.CpuMonitorService; -import com.android.server.crashrecovery.CrashRecoveryModule; +import com.android.server.crashrecovery.CrashRecoveryAdaptor; import com.android.server.credentials.CredentialManagerService; import com.android.server.criticalevents.CriticalEventLog; import com.android.server.devicepolicy.DevicePolicyManagerService; @@ -1230,12 +1230,12 @@ public final class SystemServer implements Dumpable { if (!Flags.refactorCrashrecovery()) { // Initialize RescueParty. - RescueParty.registerHealthObserver(mSystemContext); + CrashRecoveryAdaptor.rescuePartyRegisterHealthObserver(mSystemContext); if (!Flags.recoverabilityDetection()) { // Now that we have the bare essentials of the OS up and running, take // note that we just booted, which might send out a rescue party if // we're stuck in a runtime restart loop. - PackageWatchdog.getInstance(mSystemContext).noteBoot(); + CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext); } } @@ -1617,7 +1617,7 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(ROLE_SERVICE_CLASS); t.traceEnd(); - if (android.app.supervision.flags.Flags.supervisionApi()) { + if (!isWatch && android.app.supervision.flags.Flags.supervisionApi()) { t.traceBegin("StartSupervisionService"); mSystemServiceManager.startService(SupervisionService.Lifecycle.class); t.traceEnd(); @@ -2979,7 +2979,7 @@ public final class SystemServer implements Dumpable { if (Flags.refactorCrashrecovery()) { t.traceBegin("StartCrashRecoveryModule"); - mSystemServiceManager.startService(CrashRecoveryModule.Lifecycle.class); + CrashRecoveryAdaptor.initializeCrashrecoveryModuleService(mSystemServiceManager); t.traceEnd(); } else { if (Flags.recoverabilityDetection()) { @@ -2987,7 +2987,7 @@ public final class SystemServer implements Dumpable { // with package watchdog. // Note that we just booted, which might send out a rescue party if we're stuck in a // runtime restart loop. - PackageWatchdog.getInstance(mSystemContext).noteBoot(); + CrashRecoveryAdaptor.packageWatchdogNoteBoot(mSystemContext); } } diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index cc340c0a5f79..891c3349a43f 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -58,6 +58,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; @@ -1737,6 +1738,11 @@ public class MidiService extends IMidiManager.Stub { pw.decreaseIndent(); } + @Override + protected void onUnhandledException(int code, int flags, Exception e) { + Slog.wtf(TAG, "Uncaught exception in AudioService: " + code + ", " + flags, e); + } + @GuardedBy("mUsbMidiLock") private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) { String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 94eab9cda968..3b5a386dd303 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -120,6 +120,7 @@ import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.mock.MockContentResolver; +import android.util.Slog; import android.util.SparseArray; import android.util.Spline; import android.view.ContentRecordingSession; @@ -138,11 +139,14 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.app.IBatteryStats; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.internal.util.test.LocalServiceKeeperRule; import com.android.modules.utils.testing.ExtendedMockitoRule; +import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.am.BatteryStatsService; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.display.DisplayManagerService.DeviceStateListener; import com.android.server.display.DisplayManagerService.SyncRoot; @@ -153,6 +157,7 @@ import com.android.server.display.layout.Layout; import com.android.server.display.notifications.DisplayNotificationManager; import com.android.server.input.InputManagerInternal; import com.android.server.lights.LightsManager; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.sensors.SensorManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -376,6 +381,10 @@ public class DisplayManagerServiceTest { @Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor; @Mock DisplayManagerFlags mMockFlags; + @Mock WindowManagerPolicy mMockedWindowManagerPolicy; + + @Mock IBatteryStats mMockedBatteryStats; + @Rule public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this) @@ -2760,6 +2769,78 @@ public class DisplayManagerServiceTest { } @Test + public void testConnectExternalDisplay_withDisplayManagement_allowsEnableAndDisableDisplay() { + when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); + when(mMockFlags.isApplyDisplayChangedDuringDisplayAddedEnabled()).thenReturn(true); + manageDisplaysPermission(/* granted= */ true); + LocalServices.addService(WindowManagerPolicy.class, mMockedWindowManagerPolicy); + BatteryStatsService.overrideService(mMockedBatteryStats); + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + localService.registerDisplayGroupListener(callback); + + // Create default display device + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice defaultDisplayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + LogicalDisplay defaultDisplay = + logicalDisplayMapper.getDisplayLocked(defaultDisplayDevice, false); + callback.clear(); + + // Create external display device + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, new float[]{60f}, + Display.TYPE_EXTERNAL, callback); + callback.waitForExpectedEvent(); + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + assertThat(display.isEnabledLocked()).isTrue(); + callback.clear(); + + callback.expectsEvent(FakeDisplayDevice.COMMITTED_DISPLAY_STATE_CHANGED); + initDisplayPowerController(localService); + // Initial power request, should have happened from PowerManagerService. + localService.requestPowerState(defaultDisplay.getDisplayInfoLocked().displayGroupId, + new DisplayManagerInternal.DisplayPowerRequest(), + /*waitForNegativeProximity=*/ false); + localService.requestPowerState(display.getDisplayInfoLocked().displayGroupId, + new DisplayManagerInternal.DisplayPowerRequest(), + /*waitForNegativeProximity=*/ false); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + callback.waitForExpectedEvent(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_OFF); + assertThat(display.isEnabledLocked()).isFalse(); + assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_CONNECTED, + EVENT_DISPLAY_REMOVED).inOrder(); + + int displayId = display.getDisplayIdLocked(); + boolean enabled = display.isEnabledLocked(); + assertThat(enabled).isFalse(); + + for (int i = 0; i < 9; i++) { + callback.expectsEvent(FakeDisplayDevice.COMMITTED_DISPLAY_STATE_CHANGED); + enabled = !enabled; + Slog.d("DisplayManagerServiceTest", "enabled=" + enabled); + displayManager.enableConnectedDisplay(displayId, enabled); + callback.waitForExpectedEvent(); + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(enabled ? Display.STATE_ON : Display.STATE_OFF); + assertThat(defaultDisplayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + } + callback.expectsEvent(FakeDisplayDevice.COMMITTED_DISPLAY_STATE_CHANGED); + callback.waitForNonExpectedEvent(); + } + + @Test public void testConnectInternalDisplay_withDisplayManagement_shouldConnectAndAddDisplay() { when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); @@ -2795,7 +2876,7 @@ public class DisplayManagerServiceTest { displayManager.setDisplayState(display.getDisplayIdLocked(), Display.STATE_ON); assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) - .isEqualTo(Display.STATE_ON); + .isEqualTo(Display.STATE_UNKNOWN); assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), Display.STATE_OFF)).isTrue(); @@ -2828,7 +2909,7 @@ public class DisplayManagerServiceTest { var displayId = display.getDisplayIdLocked(); assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) - .isEqualTo(Display.STATE_ON); + .isEqualTo(Display.STATE_UNKNOWN); assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, Display.STATE_UNKNOWN)); @@ -3786,7 +3867,16 @@ public class DisplayManagerServiceTest { float[] refreshRates, float[] vsyncRates, int displayType) { + return createFakeDisplayDevice(displayManager, refreshRates, vsyncRates, displayType, null); + } + + private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager, + float[] refreshRates, + float[] vsyncRates, + int displayType, + FakeDisplayManagerCallback callback) { FakeDisplayDevice displayDevice = new FakeDisplayDevice(); + displayDevice.setCallback(callback); DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); int width = 100; int height = 200; @@ -3796,6 +3886,7 @@ public class DisplayManagerServiceTest { new Display.Mode(i + 1, width, height, refreshRates[i], vsyncRates[i], new float[0], new int[0]); } + displayDeviceInfo.name = "" + displayType; displayDeviceInfo.modeId = 1; displayDeviceInfo.type = displayType; displayDeviceInfo.renderFrameRate = displayDeviceInfo.supportedModes[0].getRefreshRate(); @@ -3903,6 +3994,19 @@ public class DisplayManagerServiceTest { } } + void waitForNonExpectedEvent() { + waitForNonExpectedEvent(Duration.ofSeconds(1)); + } + + void waitForNonExpectedEvent(Duration timeout) { + try { + assertWithMessage("Non Expected '" + mExpectedEvent + "'") + .that(mLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS)).isFalse(); + } catch (InterruptedException ex) { + throw new AssertionError("Waiting for expected event interrupted", ex); + } + } + private void eventSeen(String event) { if (event.equals(mExpectedEvent)) { mLatch.countDown(); @@ -3973,8 +4077,10 @@ public class DisplayManagerServiceTest { } private class FakeDisplayDevice extends DisplayDevice { + static final String COMMITTED_DISPLAY_STATE_CHANGED = "requestDisplayStateLocked"; private DisplayDeviceInfo mDisplayDeviceInfo; private Display.Mode mPreferredMode = new Display.Mode.Builder().build(); + private FakeDisplayManagerCallback mCallback; FakeDisplayDevice() { super(mMockDisplayAdapter, /* displayToken= */ null, /* uniqueId= */ "", mContext); @@ -3982,7 +4088,7 @@ public class DisplayManagerServiceTest { public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { mDisplayDeviceInfo = displayDeviceInfo; - mDisplayDeviceInfo.committedState = Display.STATE_ON; + mDisplayDeviceInfo.committedState = Display.STATE_UNKNOWN; } @Override @@ -4019,7 +4125,23 @@ public class DisplayManagerServiceTest { final float brightnessState, final float sdrBrightnessState, @Nullable DisplayOffloadSessionImpl displayOffloadSession) { - return () -> mDisplayDeviceInfo.committedState = state; + return () -> { + Slog.d("FakeDisplayDevice", mDisplayDeviceInfo.name + + " new state=" + state); + if (state != mDisplayDeviceInfo.committedState) { + Slog.d("FakeDisplayDevice", mDisplayDeviceInfo.name + + " mDisplayDeviceInfo.committedState=" + + mDisplayDeviceInfo.committedState + " set to " + state); + mDisplayDeviceInfo.committedState = state; + if (mCallback != null) { + mCallback.eventSeen(COMMITTED_DISPLAY_STATE_CHANGED); + } + } + }; + } + + void setCallback(FakeDisplayManagerCallback callback) { + this.mCallback = callback; } } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 01b2d3e34bdc..fdf6b809fa85 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -2343,6 +2343,41 @@ public final class DisplayPowerControllerTest { } } + @Test + public void stylusUsageStarted_disablesAutomaticBrightnessStrategy() { + when(mDisplayManagerFlagsMock.isBlockAutobrightnessChangesOnStylusUsage()) + .thenReturn(true); + when(mDisplayManagerFlagsMock.isRefactorDisplayPowerControllerEnabled()) + .thenReturn(true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession); + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + advanceTime(2); + clearInvocations(mHolder.automaticBrightnessController); + mHolder.dpc.stylusGestureStarted(2000000); + + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + verify(mHolder.automaticBrightnessController, times(0)) + .getAutomaticScreenBrightness(any()); + + // Stylus usage timed out, hence autobrightness is now enabled back again + advanceTime(6); + verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness(null); + + // Ideally we should be able to assert against new BrightnessEvent(Display.DEFAULT_DISPLAY), + // but because brightnessEvent has the mTime field which refers to the current time, + // asserting against that is non-trivial + verify(mHolder.automaticBrightnessController).getAutomaticScreenBrightness( + any(BrightnessEvent.class)); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ @@ -2406,6 +2441,7 @@ public final class DisplayPowerControllerTest { .thenReturn(new int[0]); when(displayDeviceConfigMock.getDefaultDozeBrightness()) .thenReturn(DEFAULT_DOZE_BRIGHTNESS); + when(displayDeviceConfigMock.getIdleStylusTimeoutMillis()).thenReturn(5); when(displayDeviceConfigMock.getBrightnessRampFastDecrease()) .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE); diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java index d831cf8a3643..b6da3ae6a5cd 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -51,6 +51,7 @@ 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.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -209,8 +210,8 @@ public class LogicalDisplayMapperTest { when(mResourcesMock.getIntArray( com.android.internal.R.array.config_deviceStatesOnWhichToSleep)) .thenReturn(new int[]{0}); - when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any())).thenAnswer( - AdditionalAnswers.returnsSecondArg()); + when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any(), anyBoolean())) + .thenAnswer(AdditionalAnswers.returnsSecondArg()); when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(false); mLooper = new TestLooper(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java index 8936f061963c..b002a1f73006 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -87,7 +88,7 @@ public class LogicalDisplayTest { mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)}; when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo); - when(mSyntheticModeManager.createAppSupportedModes(any(), any())).thenAnswer( + when(mSyntheticModeManager.createAppSupportedModes(any(), any(), anyBoolean())).thenAnswer( AdditionalAnswers.returnsSecondArg()); // Disable binder caches in this process. @@ -582,7 +583,8 @@ public class LogicalDisplayTest { Display.Mode[] appSupportedModes = new Display.Mode[] {new Display.Mode(OTHER_MODE_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 45)}; when(mSyntheticModeManager.createAppSupportedModes( - any(), eq(mDisplayDeviceInfo.supportedModes))).thenReturn(appSupportedModes); + any(), eq(mDisplayDeviceInfo.supportedModes), anyBoolean())) + .thenReturn(appSupportedModes); mLogicalDisplay.updateLocked(mDeviceRepo, mSyntheticModeManager); DisplayInfo info = mLogicalDisplay.getDisplayInfoLocked(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java index 04b79b4f1761..d93ee84f4870 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java @@ -74,7 +74,7 @@ public final class BrightnessReasonTest { @Test public void setModifierDoesntSetIfModifierIsBeyondExtremes() { - int extremeModifier = 0x40; // equal to BrightnessReason.MODIFIER_MASK * 2 + int extremeModifier = 0x80; // reset modifier mBrightnessReason.setModifier(0); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index c0698756a3d7..b3baa5deb4a7 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -125,7 +125,7 @@ public final class DisplayBrightnessControllerTest { DisplayManagerInternal.DisplayOffloadSession.class)); verify(displayBrightnessStrategy).updateBrightness( eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS, - /* userSetBrightnessChanged= */ false))); + /* userSetBrightnessChanged= */ false, /* isStylusBeingUsed */ false))); assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(), displayBrightnessStrategy); } @@ -559,4 +559,11 @@ public final class DisplayBrightnessControllerTest { displayDeviceConfig, handler, brightnessMappingStrategy, isDisplayEnabled, leadDisplayId); } + + @Test + public void setStylusBeingUsed_setsStylusInUseState() { + assertFalse(mDisplayBrightnessController.isStylusBeingUsed()); + mDisplayBrightnessController.setStylusBeingUsed(true); + assertTrue(mDisplayBrightnessController.isStylusBeingUsed()); + } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java index a44c517ed9cf..fe1505162e24 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java @@ -68,6 +68,8 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) public final class DisplayBrightnessStrategySelectorTest { private static final boolean DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING = false; + private static final boolean STYLUS_IS_NOT_BEING_USED = false; + private static final boolean STYLUS_IS_BEING_USED = true; private static final int DISPLAY_ID = 1; @Mock @@ -196,7 +198,8 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -212,7 +215,8 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -226,7 +230,8 @@ public final class DisplayBrightnessStrategySelectorTest { DISALLOW_AUTO_BRIGHTNESS_WHILE_DOZING); assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -249,7 +254,8 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_DOZE, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mDozeBrightnessModeStrategy); } @@ -259,7 +265,8 @@ public final class DisplayBrightnessStrategySelectorTest { DisplayManagerInternal.DisplayPowerRequest.class); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_OFF, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mScreenOffBrightnessModeStrategy); } @@ -271,7 +278,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mOverrideBrightnessStrategy); } @@ -284,7 +292,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mTemporaryBrightnessStrategy); } @@ -298,7 +307,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mBoostBrightnessStrategy); } @@ -312,7 +322,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(Float.NaN); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mInvalidBrightnessStrategy); } @@ -323,7 +334,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mFollowerBrightnessStrategy); } @@ -341,7 +353,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mOffloadBrightnessStrategy.getOffloadScreenBrightness()).thenReturn(0.3f); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mOffloadBrightnessStrategy); } @@ -365,7 +378,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mAutomaticBrightnessStrategy); verify(mAutomaticBrightnessStrategy).setAutoBrightnessState(Display.STATE_ON, true, BrightnessReason.REASON_UNKNOWN, @@ -373,6 +387,32 @@ public final class DisplayBrightnessStrategySelectorTest { /* useNormalBrightnessForDoze= */ false, 0.1f, false); } + + @Test + public void selectStrategy_doesNotSelectAutomaticStrategyWhenStylusInUse() { + when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); + when(mDisplayManagerFlags.offloadControlsDozeAutoBrightness()).thenReturn(true); + when(mDisplayManagerFlags.isDisplayOffloadEnabled()).thenReturn(true); + when(mDisplayOffloadSession.allowAutoBrightnessInDoze()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_allowAutoBrightnessWhileDozing)).thenReturn( + true); + mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext, + mInjector, DISPLAY_ID, mDisplayManagerFlags); + DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock( + DisplayManagerInternal.DisplayPowerRequest.class); + displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + displayPowerRequest.screenBrightnessOverride = Float.NaN; + when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN); + when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN); + when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(true); + when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(true); + assertNotEquals(mDisplayBrightnessStrategySelector.selectStrategy( + new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_BEING_USED)), + mAutomaticBrightnessStrategy); + } + @Test public void selectStrategy_selectsAutomaticFallbackStrategyWhenValid() { when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true); @@ -389,7 +429,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutoBrightnessFallbackStrategy.isValid()).thenReturn(true); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mAutoBrightnessFallbackStrategy); } @@ -407,7 +448,8 @@ public final class DisplayBrightnessStrategySelectorTest { assertNotEquals(mOffloadBrightnessStrategy, mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession))); + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED))); } @Test @@ -425,7 +467,8 @@ public final class DisplayBrightnessStrategySelectorTest { when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false); assertEquals(mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)), + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)), mFallbackBrightnessStrategy); } @@ -440,7 +483,8 @@ public final class DisplayBrightnessStrategySelectorTest { mDisplayBrightnessStrategySelector.selectStrategy( new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON, - 0.1f, false, mDisplayOffloadSession)); + 0.1f, false, mDisplayOffloadSession, + STYLUS_IS_NOT_BEING_USED)); StrategySelectionNotifyRequest strategySelectionNotifyRequest = new StrategySelectionNotifyRequest(displayPowerRequest, Display.STATE_ON, diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java index 99dfa739fb80..2a71af06e0c2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java @@ -129,7 +129,8 @@ public class AutoBrightnessFallbackStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mAutoBrightnessFallbackStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index efa8b3ef775f..8a1f86093ecf 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -637,7 +637,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } @@ -686,7 +686,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } @@ -725,7 +725,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } @@ -764,7 +764,7 @@ public class AutomaticBrightnessStrategyTest { .build(); DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java index 275bb3efee8e..c03309e8b4a4 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java @@ -60,7 +60,8 @@ public class BoostBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mBoostBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java index 23e447c25245..e7f80b04e669 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java @@ -57,7 +57,8 @@ public class DozeBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mDozeBrightnessModeStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java index c4a579092d38..dcfa174a53f5 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java @@ -61,7 +61,8 @@ public class FallbackBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mFallbackBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, currentBrightness, - /* userSetBrightnessChanged= */ true)); + /* userSetBrightnessChanged= */ true, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java index c01f96e800de..239cdb6002e9 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java @@ -61,7 +61,8 @@ public class FollowerBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mFollowerBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java index 9fb2afa26ed2..77302f8747c1 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java @@ -72,7 +72,8 @@ public class OffloadBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mOffloadBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy .getOffloadScreenBrightness(), 0.0f); diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java index e8b4c06b9c89..cc21af19722b 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java @@ -60,7 +60,8 @@ public class OverrideBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mOverrideBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java index 38709ece7007..652663e52a0a 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java @@ -58,7 +58,8 @@ public final class ScreenOffBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mScreenOffBrightnessModeStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java index f523b6af426b..0022cab371e8 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java @@ -60,7 +60,8 @@ public class TemporaryBrightnessStrategyTest { DisplayBrightnessState updatedDisplayBrightnessState = mTemporaryBrightnessStrategy.updateBrightness( new StrategyExecutionRequest(displayPowerRequest, 0.2f, - /* userSetBrightnessChanged= */ false)); + /* userSetBrightnessChanged= */ false, + /* isStylusBeingUsed */ false)); assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt index b2d83d744ce6..9a93fba040cc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SyntheticModeManagerTest.kt @@ -43,35 +43,42 @@ class SyntheticModeManagerTest { @Test fun testAppSupportedModes(@TestParameter testCase: AppSupportedModesTestCase) { whenever(mockFlags.isSynthetic60HzModesEnabled).thenReturn(testCase.syntheticModesEnabled) + whenever(mockFlags.hasArrSupportFlag()).thenReturn(testCase.hasArrSupport) whenever(mockConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported) val syntheticModeManager = SyntheticModeManager(mockFlags) val result = syntheticModeManager.createAppSupportedModes( - mockConfig, testCase.supportedModes) + mockConfig, testCase.supportedModes, testCase.hasArrSupport) assertThat(result).isEqualTo(testCase.expectedAppModes) } + // TODO(b/361433651) Remove vrrSupported once hasArrSupport is rolled out enum class AppSupportedModesTestCase( val syntheticModesEnabled: Boolean, val vrrSupported: Boolean, + val hasArrSupport: Boolean, val supportedModes: Array<Mode>, val expectedAppModes: Array<Mode> ) { - SYNTHETIC_MODES_NOT_SUPPORTED(false, true, DISPLAY_MODES, DISPLAY_MODES), - VRR_NOT_SUPPORTED(true, false, DISPLAY_MODES, DISPLAY_MODES), - VRR_SYNTHETIC_NOT_SUPPORTED(false, false, DISPLAY_MODES, DISPLAY_MODES), - SINGLE_RESOLUTION_MODES(true, true, DISPLAY_MODES, arrayOf( + SYNTHETIC_MODES_NOT_SUPPORTED(false, true, true, DISPLAY_MODES, DISPLAY_MODES), + VRR_NOT_SUPPORTED(true, false, false, DISPLAY_MODES, DISPLAY_MODES), + VRR_SYNTHETIC_NOT_SUPPORTED(false, false, false, DISPLAY_MODES, DISPLAY_MODES), + SINGLE_RESOLUTION_MODES(true, true, true, DISPLAY_MODES, arrayOf( Mode(2, 100, 100, 120f), Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf()) )), - NO_60HZ_MODES(true, true, arrayOf(Mode(2, 100, 100, 120f)), + SINGLE_RESOLUTION_MODES_HASARR(true, false, true, DISPLAY_MODES, arrayOf( + Mode(2, 100, 100, 120f), + Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf()) + )), + NO_60HZ_MODES(true, true, true, arrayOf(Mode(2, 100, 100, 120f)), arrayOf( Mode(2, 100, 100, 120f), Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf()) ) ), - MULTI_RESOLUTION_MODES(true, true, + MULTI_RESOLUTION_MODES(true, true, true, arrayOf( Mode(1, 100, 100, 120f), Mode(2, 200, 200, 60f), @@ -86,7 +93,7 @@ class SyntheticModeManagerTest { Mode(7, 300, 300, 60f, 60f, true, floatArrayOf(), intArrayOf()) ) ), - WITH_HDR_TYPES(true, true, + WITH_HDR_TYPES(true, true, true, arrayOf( Mode(1, 100, 100, 120f, 120f, false, floatArrayOf(), intArrayOf(1, 2)), Mode(2, 200, 200, 60f, 120f, false, floatArrayOf(), intArrayOf(3, 4)), @@ -99,7 +106,7 @@ class SyntheticModeManagerTest { Mode(5, 200, 200, 60f, 60f, true, floatArrayOf(), intArrayOf(5, 6)), ) ), - UNACHIEVABLE_60HZ(true, true, + UNACHIEVABLE_60HZ(true, true, true, arrayOf( Mode(1, 100, 100, 90f), ), @@ -107,7 +114,7 @@ class SyntheticModeManagerTest { Mode(1, 100, 100, 90f), ) ), - MULTI_RESOLUTION_MODES_WITH_UNACHIEVABLE_60HZ(true, true, + MULTI_RESOLUTION_MODES_WITH_UNACHIEVABLE_60HZ(true, true, true, arrayOf( Mode(1, 100, 100, 120f), Mode(2, 200, 200, 90f), @@ -118,7 +125,7 @@ class SyntheticModeManagerTest { Mode(3, 100, 100, 60f, 60f, true, floatArrayOf(), intArrayOf()), ) ), - LOWER_THAN_60HZ_MODES(true, true, + LOWER_THAN_60HZ_MODES(true, true, true, arrayOf( Mode(1, 100, 100, 30f), Mode(2, 100, 100, 45f), diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java index 121145672d68..439243e85e75 100644 --- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java +++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java @@ -82,6 +82,11 @@ public class AudioManagerRouteControllerTest { private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET = createAudioDeviceInfo( AudioSystem.DEVICE_OUT_WIRED_HEADSET, "name_wired_hs", /* address= */ null); + private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS = + createAudioDeviceInfo( + AudioSystem.DEVICE_OUT_WIRED_HEADSET, + "name_wired_hs_with_address", + /* address= */ "card=1;device=0"); private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP = createAudioDeviceInfo( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "name_a2dp", /* address= */ "12:34:45"); @@ -304,6 +309,55 @@ public class AudioManagerRouteControllerTest { assertThat(selectedRoute.getName().toString()).isEqualTo(FAKE_ROUTE_NAME); } + @Test + public void getAvailableRoutes_whenAddressIsPopulatedForNonBluetoothDevice_usesCorrectName() { + addAvailableAudioDeviceInfo( + /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS, + /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS, + FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP); + + List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes(); + assertThat(availableRoutes.size()).isEqualTo(3); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET) + .getName() + .toString()) + .isEqualTo( + FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET_WITH_ADDRESS + .getProductName() + .toString()); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP) + .getName() + .toString()) + .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP.getProductName().toString()); + } + + @Test + public void + getAvailableRoutes_whenAddressIsNotPopulatedForNonBluetoothDevice_usesCorrectName() { + addAvailableAudioDeviceInfo( + /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET, + /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET); + + List<MediaRoute2Info> availableRoutes = mControllerUnderTest.getAvailableRoutes(); + assertThat(availableRoutes.size()).isEqualTo(2); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER) + .getName() + .toString()) + .isEqualTo(FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER.getProductName().toString()); + + assertThat( + getAvailableRouteWithType(MediaRoute2Info.TYPE_WIRED_HEADSET) + .getName() + .toString()) + .isEqualTo(FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET.getProductName().toString()); + } + // Internal methods. @NonNull diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 412599d463c5..7dbd0578df0d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -1540,7 +1540,8 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_Provider_Self() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); - bindProvider(app, app, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(app, cpr); updateOomAdj(app); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ @@ -1555,7 +1556,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindProvider(app, client, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(client, cpr); mProcessStateController.setTreatLikeActivity(client.mServices, true); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client); @@ -1572,7 +1574,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindProvider(app, client, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(client, cpr); doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); doReturn(client).when(mService).getTopApp(); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -1590,7 +1593,8 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); mProcessStateController.setHasForegroundServices(client.mServices, true, 0, true); - bindProvider(app, client, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(client, cpr); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1620,7 +1624,8 @@ public class MockingOomAdjusterTests { mProcessStateController.setHasForegroundServices(client.mServices, true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, false); - bindProvider(app, client, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(client, cpr); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1642,7 +1647,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindProvider(app, client, null, null, true); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, true); + bindProvider(client, cpr); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); @@ -1655,9 +1661,24 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_Provider_Retention() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); - app.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); + ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + final String providerName = "aProvider"; + // Go through the motions of binding a provider + final ContentProviderRecord cpr = createContentProviderRecord(app, providerName, false); + final ContentProviderConnection conn = bindProvider(client, cpr); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); - updateOomAdj(app); + updateOomAdj(client, app); + + assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); + + unbindProvider(client, cpr, conn); + mProcessStateController.removePublishedProvider(app, providerName); + final long lastProviderTime = SystemClock.uptimeMillis(); + mProcessStateController.setLastProviderTime(app, SystemClock.uptimeMillis()); + updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, SCHED_GROUP_BACKGROUND, "recent-provider"); @@ -1667,7 +1688,9 @@ public class MockingOomAdjusterTests { final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + setProcessesToLru(client, app); mProcessStateController.runFollowUpUpdate(); final int expectedAdj = mService.mConstants.USE_TIERED_CACHED_ADJ @@ -2019,7 +2042,8 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindProvider(client, client2, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(client, null, false); + bindProvider(client2, cpr); mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); @@ -2039,7 +2063,8 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, 0, mock(IBinder.class)); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindProvider(client, client2, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(client, null, false); + bindProvider(client2, cpr); mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true); bindService(client2, app, null, null, 0, mock(IBinder.class)); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); @@ -2057,10 +2082,12 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindProvider(app, client, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(client, cpr); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindProvider(client, client2, null, null, false); + final ContentProviderRecord cpr2 = createContentProviderRecord(client, null, false); + bindProvider(client2, cpr2); mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); @@ -2077,12 +2104,15 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - bindProvider(app, client, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(client, cpr); ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindProvider(client, client2, null, null, false); + final ContentProviderRecord cpr2 = createContentProviderRecord(client, null, false); + bindProvider(client2, cpr2); mProcessStateController.setHasForegroundServices(client2.mServices, true, 0, true); - bindProvider(client2, app, null, null, false); + final ContentProviderRecord cpr3 = createContentProviderRecord(client2, null, false); + bindProvider(app, cpr3); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); @@ -2629,21 +2659,24 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); - ContentProviderRecord cr = bindProvider(app, app2, null, null, false); + final ContentProviderRecord cpr = createContentProviderRecord(app, null, false); + bindProvider(app2, cpr); ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); - bindProvider(app2, app3, null, null, false); - bindProvider(app3, app, null, null, false); + final ContentProviderRecord cpr2 = createContentProviderRecord(app2, null, false); + bindProvider(app3, cpr2); + final ContentProviderRecord cpr3 = createContentProviderRecord(app3, null, false); + bindProvider(app, cpr3); WindowProcessController wpc = app3.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); ProcessRecord app4 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID, MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); mProcessStateController.setHasOverlayUi(app4, true); - bindProvider(app, app4, cr, null, false); + bindProvider(app4, cpr); ProcessRecord app5 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); mProcessStateController.setHasForegroundServices(app5.mServices, true, 0, true); - bindProvider(app, app5, cr, null, false); + bindProvider(app5, cpr); setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); @@ -3248,21 +3281,30 @@ public class MockingOomAdjusterTests { } } - private ContentProviderRecord bindProvider(ProcessRecord publisher, ProcessRecord client, - ContentProviderRecord record, String name, boolean hasExternalProviders) { - if (record == null) { - record = mock(ContentProviderRecord.class); - mProcessStateController.addPublishedProvider(publisher, name, record); - record.proc = publisher; - setFieldValue(ContentProviderRecord.class, record, "connections", - new ArrayList<ContentProviderConnection>()); - doReturn(hasExternalProviders).when(record).hasExternalProcessHandles(); - } + private ContentProviderRecord createContentProviderRecord(ProcessRecord publisher, String name, + boolean hasExternalProviders) { + ContentProviderRecord record = mock(ContentProviderRecord.class); + mProcessStateController.addPublishedProvider(publisher, name, record); + record.proc = publisher; + setFieldValue(ContentProviderRecord.class, record, "connections", + new ArrayList<ContentProviderConnection>()); + doReturn(hasExternalProviders).when(record).hasExternalProcessHandles(); + return record; + } + + private ContentProviderConnection bindProvider(ProcessRecord client, + ContentProviderRecord record) { ContentProviderConnection conn = spy(new ContentProviderConnection(record, client, client.info.packageName, UserHandle.getUserId(client.uid))); record.connections.add(conn); mProcessStateController.addProviderConnection(client, conn); - return record; + return conn; + } + + private void unbindProvider(ProcessRecord client, ContentProviderRecord record, + ContentProviderConnection conn) { + record.connections.remove(conn); + mProcessStateController.removeProviderConnection(client, conn); } @SuppressWarnings("GuardedBy") diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index 40b9c61a0597..9781851da7e6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -95,7 +95,6 @@ import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.QcConstants; -import com.android.server.job.controllers.QuotaController.QuotaBump; import com.android.server.job.controllers.QuotaController.ShrinkableDebits; import com.android.server.job.controllers.QuotaController.TimedEvent; import com.android.server.job.controllers.QuotaController.TimingSession; @@ -136,7 +135,6 @@ public class QuotaControllerTest { private QuotaController.QcConstants mQcConstants; private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants(); private int mSourceUid; - private AppStandbyInternal.AppIdleStateChangeListener mAppIdleStateChangeListener; private PowerAllowlistInternal.TempAllowlistChangeListener mTempAllowlistListener; private IUidObserver mUidObserver; private UsageStatsManagerInternal.UsageEventListener mUsageEventListener; @@ -191,8 +189,7 @@ public class QuotaControllerTest { when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); doReturn(mActivityMangerInternal) .when(() -> LocalServices.getService(ActivityManagerInternal.class)); - final AppStandbyInternal appStandbyInternal = mock(AppStandbyInternal.class); - doReturn(appStandbyInternal) + doReturn(mock(AppStandbyInternal.class)) .when(() -> LocalServices.getService(AppStandbyInternal.class)); doReturn(mock(BatteryManagerInternal.class)) .when(() -> LocalServices.getService(BatteryManagerInternal.class)); @@ -239,8 +236,6 @@ public class QuotaControllerTest { // Initialize real objects. // Capture the listeners. - ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> aiscListenerCaptor = - ArgumentCaptor.forClass(AppStandbyInternal.AppIdleStateChangeListener.class); ArgumentCaptor<IUidObserver> uidObserverCaptor = ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<PowerAllowlistInternal.TempAllowlistChangeListener> taChangeCaptor = @@ -250,8 +245,6 @@ public class QuotaControllerTest { mQuotaController = new QuotaController(mJobSchedulerService, mock(BackgroundJobsController.class), mock(ConnectivityController.class)); - verify(appStandbyInternal).addListener(aiscListenerCaptor.capture()); - mAppIdleStateChangeListener = aiscListenerCaptor.getValue(); verify(mPowerAllowlistInternal) .registerTempAllowlistChangeListener(taChangeCaptor.capture()); mTempAllowlistListener = taChangeCaptor.getValue(); @@ -488,14 +481,12 @@ public class QuotaControllerTest { now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3); TimingSession two = createTimingSession( now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); - QuotaBump bump1 = new QuotaBump(now - 2 * HOUR_IN_MILLIS); TimingSession thr = createTimingSession( now - (3 * HOUR_IN_MILLIS + 10 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1); // Overlaps 24 hour boundary. TimingSession fou = createTimingSession( now - (24 * HOUR_IN_MILLIS + 2 * MINUTE_IN_MILLIS), 7 * MINUTE_IN_MILLIS, 1); // Way past the 24 hour boundary. - QuotaBump bump2 = new QuotaBump(now - 24 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); TimingSession fiv = createTimingSession( now - (25 * HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 4); List<TimedEvent> expectedRegular = new ArrayList<>(); @@ -503,16 +494,13 @@ public class QuotaControllerTest { // Added in correct (chronological) order. expectedRegular.add(fou); expectedRegular.add(thr); - expectedRegular.add(bump1); expectedRegular.add(two); expectedRegular.add(one); expectedEJ.add(fou); expectedEJ.add(one); mQuotaController.saveTimingSession(0, "com.android.test", fiv, false); - mQuotaController.getTimingSessions(0, "com.android.test").add(bump2); mQuotaController.saveTimingSession(0, "com.android.test", fou, false); mQuotaController.saveTimingSession(0, "com.android.test", thr, false); - mQuotaController.getTimingSessions(0, "com.android.test").add(bump1); mQuotaController.saveTimingSession(0, "com.android.test", two, false); mQuotaController.saveTimingSession(0, "com.android.test", one, false); mQuotaController.saveTimingSession(0, "com.android.test", fiv, true); @@ -945,19 +933,22 @@ public class QuotaControllerTest { public void testGetExecutionStatsLocked_Values() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (23 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - (mQcConstants.WINDOW_SIZE_RARE_MS - HOUR_IN_MILLIS), + 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - HOUR_IN_MILLIS), + 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, + 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); ExecutionStats expectedStats = new ExecutionStats(); // Active - expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS; - expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS; + expectedStats.allowedTimePerPeriodMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_ACTIVE_MS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE; expectedStats.expirationTimeElapsed = now + 4 * MINUTE_IN_MILLIS; @@ -972,7 +963,7 @@ public class QuotaControllerTest { } // Working - expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_WORKING_MS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_WORKING; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_WORKING; expectedStats.expirationTimeElapsed = now; @@ -989,7 +980,7 @@ public class QuotaControllerTest { } // Frequent - expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_FREQUENT_MS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_FREQUENT; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_FREQUENT; expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; @@ -1007,7 +998,7 @@ public class QuotaControllerTest { } // Rare - expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS; + expectedStats.windowSizeMs = mQcConstants.WINDOW_SIZE_RARE_MS; expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE; expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE; expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS; @@ -1252,7 +1243,8 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (7 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", - createTimingSession(now - (2 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, + 10 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(0, "com.android.test", createTimingSession(now - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); final ExecutionStats originalStatsActive; @@ -1658,7 +1650,7 @@ public class QuotaControllerTest { // Close to boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - (24 * HOUR_IN_MILLIS - MINUTE_IN_MILLIS), - 4 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS, 5), false); + mQcConstants.MAX_EXECUTION_TIME_MS - 5 * MINUTE_IN_MILLIS, 5), false); setStandbyBucket(WORKING_INDEX); synchronized (mQuotaController.mLock) { @@ -1674,7 +1666,8 @@ public class QuotaControllerTest { // Far from boundary. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (20 * HOUR_IN_MILLIS), 4 * HOUR_IN_MILLIS - 3 * MINUTE_IN_MILLIS, 5), + now - (20 * HOUR_IN_MILLIS), + mQcConstants.MAX_EXECUTION_TIME_MS - 3 * MINUTE_IN_MILLIS, 5), false); setStandbyBucket(WORKING_INDEX); @@ -1701,11 +1694,12 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( now - (24 * HOUR_IN_MILLIS + 11 * MINUTE_IN_MILLIS), - 4 * HOUR_IN_MILLIS, + mQcConstants.MAX_EXECUTION_TIME_MS, 5), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), + now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS + MINUTE_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); synchronized (mQuotaController.mLock) { @@ -1729,11 +1723,12 @@ public class QuotaControllerTest { mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( now - (20 * HOUR_IN_MILLIS), - 3 * HOUR_IN_MILLIS + 48 * MINUTE_IN_MILLIS, + mQcConstants.MAX_EXECUTION_TIME_MS - 12 * MINUTE_IN_MILLIS, 5), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession( - now - (8 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), + now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS + MINUTE_IN_MILLIS), + 3 * MINUTE_IN_MILLIS, 5), false); synchronized (mQuotaController.mLock) { @@ -1840,129 +1835,6 @@ public class QuotaControllerTest { } } - /** - * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket - * window and there are valid QuotaBumps in the history. - */ - @Test - public void testGetTimeUntilQuotaConsumedLocked_QuotaBump() { - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - // Close to RARE boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS), - 30 * SECOND_IN_MILLIS, 5), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS)); - // Far away from FREQUENT boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); - // Overlap WORKING_SET boundary. - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 2 * HOUR_IN_MILLIS)); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 5), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS)); - // Close to ACTIVE boundary. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false); - - setStandbyBucket(RARE_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(3 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(4 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - - setStandbyBucket(FREQUENT_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(4 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(4 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - - setStandbyBucket(WORKING_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(8 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - - // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the - // max execution time. - setStandbyBucket(ACTIVE_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(10 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - } - - /** - * Test getTimeUntilQuotaConsumedLocked when there are valid QuotaBumps in recent history that - * provide enough additional quota to bridge gaps between sessions. - */ - @Test - public void testGetTimeUntilQuotaConsumedLocked_QuotaBump_CrucialBumps() { - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (25 * HOUR_IN_MILLIS), - 30 * MINUTE_IN_MILLIS, 25), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 16 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 12 * HOUR_IN_MILLIS)); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 8 * HOUR_IN_MILLIS)); - // Without the valid quota bumps, the app would only 3 minutes until the quota was consumed. - // The quota bumps provide enough quota to bridge the gap between the two earliest sessions. - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (8 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS), - 2 * MINUTE_IN_MILLIS, 5), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE) - .add(new QuotaBump(now - 15 * MINUTE_IN_MILLIS)); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (9 * MINUTE_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 1), false); - - setStandbyBucket(FREQUENT_INDEX); - synchronized (mQuotaController.mLock) { - assertEquals(2 * MINUTE_IN_MILLIS, - mQuotaController.getRemainingExecutionTimeLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - assertEquals(7 * MINUTE_IN_MILLIS, - mQuotaController.getTimeUntilQuotaConsumedLocked( - SOURCE_USER_ID, SOURCE_PACKAGE)); - } - } - @Test public void testIsWithinQuotaLocked_NeverApp() { synchronized (mQuotaController.mLock) { @@ -2309,270 +2181,6 @@ public class QuotaControllerTest { } } - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_Duration() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 5 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession( - now - (HOUR_IN_MILLIS - 2 * MINUTE_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1), - false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 1); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(5 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_Duration", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Phase out the first session - advanceElapsedClock(5 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(7 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(6 * MINUTE_IN_MILLIS, stats.allowedTimePerPeriodMs); - } - } - - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_JobCount() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 20 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 10); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 0); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 10), false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - mQuotaController.incrementJobCountLocked(SOURCE_USER_ID, SOURCE_PACKAGE, 10); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(10, stats.jobCountLimit); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - } - - // Phase out the first session - advanceElapsedClock(3 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(12, stats.jobCountLimit); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(11, stats.jobCountLimit); - } - } - - @Test - public void testIsWithinQuotaLocked_WithQuotaBump_SessionCount() { - setDischarging(); - int standbyBucket = WORKING_INDEX; - setStandbyBucket(standbyBucket); - setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, - 20 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 2); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 0); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 8 * HOUR_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 5); - - long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (HOUR_IN_MILLIS), 5 * MINUTE_IN_MILLIS, 1), false); - mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (30 * MINUTE_IN_MILLIS), MINUTE_IN_MILLIS, 1), false); - final ExecutionStats stats; - synchronized (mQuotaController.mLock) { - stats = mQuotaController.getExecutionStatsLocked( - SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(2, stats.sessionCountLimit); - } - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - - advanceElapsedClock(HOUR_IN_MILLIS); - - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - - // Emulate a quota bump while some jobs are executing - JobStatus job1 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 1); - JobStatus job2 = createJobStatus("testIsWithinQuotaLocked_WithQuotaBump_JobCount", 2); - - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job1, null); - mQuotaController.prepareForExecutionLocked(job1); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - mAppIdleStateChangeListener.triggerTemporaryQuotaBump(SOURCE_PACKAGE, SOURCE_USER_ID); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job1, null); - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStartTrackingJobLocked(job2, null); - mQuotaController.prepareForExecutionLocked(job2); - } - - advanceElapsedClock(MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - mQuotaController.maybeStopTrackingJobLocked(job2, null); - assertFalse(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - // Phase out the first session - advanceElapsedClock(2 * MINUTE_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(4, stats.sessionCountLimit); - } - - // Phase out the first quota bump - advanceElapsedClock(7 * HOUR_IN_MILLIS); - synchronized (mQuotaController.mLock) { - assertTrue(mQuotaController - .isWithinQuotaLocked(SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX)); - assertEquals(3, stats.sessionCountLimit); - } - } @Test public void testIsWithinEJQuotaLocked_NeverApp() { @@ -2935,12 +2543,12 @@ public class QuotaControllerTest { anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); // Test with timing sessions in window but still in quota. - final long end = now - (2 * HOUR_IN_MILLIS - 5 * MINUTE_IN_MILLIS); + final long end = now - (mQcConstants.WINDOW_SIZE_WORKING_MS - 5 * MINUTE_IN_MILLIS); // Counting backwards, the quota will come back one minute before the end. - final long expectedAlarmTime = - end - MINUTE_IN_MILLIS + 2 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; + final long expectedAlarmTime = end - MINUTE_IN_MILLIS + mQcConstants.WINDOW_SIZE_WORKING_MS + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - new TimingSession(now - 2 * HOUR_IN_MILLIS, end, 1), false); + new TimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, end, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked( SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); @@ -3007,7 +2615,8 @@ public class QuotaControllerTest { // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS + - 2 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked( SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket); @@ -3016,8 +2625,9 @@ public class QuotaControllerTest { anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); // Test with timing sessions in window but still in quota. - final long start = now - (6 * HOUR_IN_MILLIS); - final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; + final long start = now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - 2 * HOUR_IN_MILLIS); + final long expectedAlarmTime = start + mQcConstants.WINDOW_SIZE_FREQUENT_MS + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { @@ -3091,7 +2701,8 @@ public class QuotaControllerTest { // Test with timing sessions out of window. final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 10 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_FREQUENT_MS + - 2 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked( SOURCE_USER_ID, SOURCE_PACKAGE, effectiveStandbyBucket); @@ -3100,8 +2711,9 @@ public class QuotaControllerTest { anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); // Test with timing sessions in window but still in quota. - final long start = now - (6 * HOUR_IN_MILLIS); - final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS + mQcConstants.IN_QUOTA_BUFFER_MS; + final long start = now - (mQcConstants.WINDOW_SIZE_FREQUENT_MS - 2 * HOUR_IN_MILLIS); + final long expectedAlarmTime = start + mQcConstants.WINDOW_SIZE_FREQUENT_MS + + mQcConstants.IN_QUOTA_BUFFER_MS; mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(start, 5 * MINUTE_IN_MILLIS, 1), false); synchronized (mQuotaController.mLock) { @@ -3273,7 +2885,7 @@ public class QuotaControllerTest { // And down from there. final long expectedWorkingAlarmTime = - outOfQuotaTime + (2 * HOUR_IN_MILLIS) + outOfQuotaTime + mQcConstants.WINDOW_SIZE_WORKING_MS + mQcConstants.IN_QUOTA_BUFFER_MS; setStandbyBucket(WORKING_INDEX, jobStatus); synchronized (mQuotaController.mLock) { @@ -3285,7 +2897,7 @@ public class QuotaControllerTest { eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); final long expectedFrequentAlarmTime = - outOfQuotaTime + (8 * HOUR_IN_MILLIS) + outOfQuotaTime + mQcConstants.WINDOW_SIZE_FREQUENT_MS + mQcConstants.IN_QUOTA_BUFFER_MS; setStandbyBucket(FREQUENT_INDEX, jobStatus); synchronized (mQuotaController.mLock) { @@ -3297,7 +2909,7 @@ public class QuotaControllerTest { eq(TAG_QUOTA_CHECK), any(), any(Handler.class)); final long expectedRareAlarmTime = - outOfQuotaTime + (24 * HOUR_IN_MILLIS) + outOfQuotaTime + mQcConstants.WINDOW_SIZE_RARE_MS + mQcConstants.IN_QUOTA_BUFFER_MS; setStandbyBucket(RARE_INDEX, jobStatus); synchronized (mQuotaController.mLock) { @@ -3457,7 +3069,7 @@ public class QuotaControllerTest { } final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - // Working set window size is 2 hours. + // Working set window size is configured with QcConstants.WINDOW_SIZE_WORKING_MS. final int standbyBucket = WORKING_INDEX; final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2; final long remainingTimeMs = @@ -3466,13 +3078,14 @@ public class QuotaControllerTest { // Session straddles edge of bucket window. Only the contribution should be counted towards // the quota. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - (2 * HOUR_IN_MILLIS + 3 * MINUTE_IN_MILLIS), - 3 * MINUTE_IN_MILLIS + contributionMs, 3), false); + createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS + - 3 * MINUTE_IN_MILLIS, 3 * MINUTE_IN_MILLIS + contributionMs, + 3), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - HOUR_IN_MILLIS, remainingTimeMs, 2), false); // Expected alarm time should be when the app will have QUOTA_BUFFER_MS time of quota, which // is 2 hours + (QUOTA_BUFFER_MS - contributionMs) after the start of the second session. - final long expectedAlarmTime = now - HOUR_IN_MILLIS + 2 * HOUR_IN_MILLIS + final long expectedAlarmTime = now - HOUR_IN_MILLIS + mQcConstants.WINDOW_SIZE_WORKING_MS + (mQcConstants.IN_QUOTA_BUFFER_MS - contributionMs); synchronized (mQuotaController.mLock) { mQuotaController.maybeScheduleStartAlarmLocked( @@ -3576,12 +3189,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 84 * SECOND_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, - 93 * SECOND_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, 92); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, 91); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 90 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, 89); assertEquals(8 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3639,11 +3246,6 @@ public class QuotaControllerTest { assertEquals(85 * SECOND_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(84 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(83 * SECOND_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(93 * SECOND_IN_MILLIS, mQuotaController.getQuotaBumpAdditionDurationMs()); - assertEquals(92, mQuotaController.getQuotaBumpAdditionJobCount()); - assertEquals(91, mQuotaController.getQuotaBumpAdditionSessionCount()); - assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); - assertEquals(89, mQuotaController.getQuotaBumpLimit()); } @Test @@ -3696,11 +3298,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_DURATION_MS, -1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_JOB_COUNT, -1); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_ADDITIONAL_SESSION_COUNT, -1); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 59 * MINUTE_IN_MILLIS); - setDeviceConfigInt(QcConstants.KEY_QUOTA_BUMP_LIMIT, -1); assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3751,11 +3348,6 @@ public class QuotaControllerTest { assertEquals(0, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(0, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(0, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionDurationMs()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionJobCount()); - assertEquals(0, mQuotaController.getQuotaBumpAdditionSessionCount()); - assertEquals(60 * MINUTE_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); - assertEquals(0, mQuotaController.getQuotaBumpLimit()); // Invalid configurations. // In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD @@ -3813,7 +3405,6 @@ public class QuotaControllerTest { setDeviceConfigLong(QcConstants.KEY_EJ_REWARD_NOTIFICATION_SEEN_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS); setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS); - setDeviceConfigLong(QcConstants.KEY_QUOTA_BUMP_WINDOW_SIZE_MS, 25 * HOUR_IN_MILLIS); assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]); @@ -3854,7 +3445,6 @@ public class QuotaControllerTest { assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getEJRewardNotificationSeenMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTempAllowlistMs()); assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJGracePeriodTopAppMs()); - assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getQuotaBumpWindowSizeMs()); } /** Tests that TimingSessions aren't saved when the device is charging. */ @@ -4734,7 +4324,7 @@ public class QuotaControllerTest { // The package only has one second to run, but this session is at the edge of the rolling // window, so as the package "reaches its quota" it will have more to keep running. mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, - createTimingSession(now - 2 * HOUR_IN_MILLIS, + createTimingSession(now - mQcConstants.WINDOW_SIZE_WORKING_MS, 10 * SECOND_IN_MILLIS - remainingTimeMs, 1), false); mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE, createTimingSession(now - HOUR_IN_MILLIS, diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java index 9ba272446689..625dbe6e16f9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundUserSoundNotifierTest.java @@ -18,6 +18,7 @@ package com.android.server.pm; import static android.media.AudioAttributes.USAGE_ALARM; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -91,6 +92,7 @@ public class BackgroundUserSoundNotifierTest { } @Test public void testAlarmOnBackgroundUser_foregroundUserNotified() throws RemoteException { + assumeTrue(UserManager.supportsMultipleUsers()); AudioAttributes aa = new AudioAttributes.Builder().setUsage(USAGE_ALARM).build(); UserInfo user = createUser("User", UserManager.USER_TYPE_FULL_SECONDARY, 0); final int fgUserId = mSpiedContext.getUserId(); @@ -100,7 +102,7 @@ public class BackgroundUserSoundNotifierTest { /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); - mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi); verify(mNotificationManager) .notifyAsUser(eq(BackgroundUserSoundNotifier.class.getSimpleName()), eq(afi.getClientUid()), any(Notification.class), @@ -116,7 +118,7 @@ public class BackgroundUserSoundNotifierTest { /* packageName= */ "com.android.car.audio", AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); - mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi); verifyZeroInteractions(mNotificationManager); } @@ -131,7 +133,7 @@ public class BackgroundUserSoundNotifierTest { AudioManager.AUDIOFOCUS_GAIN, AudioManager.AUDIOFOCUS_NONE, /* flags= */ 0, Build.VERSION.SDK_INT); clearInvocations(mNotificationManager); - mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi, mSpiedContext); + mBackgroundUserSoundNotifier.notifyForegroundUserAboutSoundIfNecessary(afi); verifyZeroInteractions(mNotificationManager); } @@ -169,7 +171,7 @@ public class BackgroundUserSoundNotifierTest { doReturn(focusStack).when(mockAudioPolicy).getFocusStack(); mBackgroundUserSoundNotifier.mFocusControlAudioPolicy = mockAudioPolicy; - mBackgroundUserSoundNotifier.muteAlarmSounds(mSpiedContext); + mBackgroundUserSoundNotifier.muteAlarmSounds(bgUserUid); verify(apc1.getPlayerProxy()).stop(); verify(mockAudioPolicy).sendFocusLossAndUpdate(afi); @@ -178,6 +180,7 @@ public class BackgroundUserSoundNotifierTest { @Test public void testOnAudioFocusGrant_alarmOnBackgroundUser_notifiesForegroundUser() { + assumeTrue(UserManager.supportsMultipleUsers()); final int fgUserId = mSpiedContext.getUserId(); UserInfo bgUser = createUser("Background User", UserManager.USER_TYPE_FULL_SECONDARY, 0); int bgUserUid = bgUser.id * 100000; @@ -205,7 +208,7 @@ public class BackgroundUserSoundNotifierTest { .when(mUserManager).getUserSwitchability(any()); Notification notification = mBackgroundUserSoundNotifier.createNotification(userName, - mSpiedContext); + mSpiedContext, 101000); assertEquals("Alarm for BgUser", notification.extras.getString( Notification.EXTRA_TITLE)); @@ -232,7 +235,7 @@ public class BackgroundUserSoundNotifierTest { .when(mUserManager).getUserSwitchability(any()); Notification notification = mBackgroundUserSoundNotifier.createNotification(userName, - mSpiedContext); + mSpiedContext, 101000); assertEquals(1, notification.actions.length); assertEquals(mSpiedContext.getString( diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java index 39acd8dd816a..43a8aa957fa5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java @@ -72,7 +72,6 @@ import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; @@ -486,7 +485,7 @@ public class StagingManagerTest { FakeStagedSession session = new FakeStagedSession(239); session.setIsApex(true); // Call and verify - Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session); + var result = mStagingManager.getStagedApexInfos(session); assertThat(result).isEmpty(); } // Invalid session: destroyed @@ -496,7 +495,7 @@ public class StagingManagerTest { session.setIsApex(true); session.setDestroyed(true); // Call and verify - Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(session); + var result = mStagingManager.getStagedApexInfos(session); assertThat(result).isEmpty(); } } @@ -520,8 +519,8 @@ public class StagingManagerTest { when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos); // Call and verify - Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(validSession); - assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0]); + List<ApexInfo> result = mStagingManager.getStagedApexInfos(validSession); + assertThat(result).containsExactly(fakeApexInfos[0]); ArgumentCaptor<ApexSessionParams> argumentCaptor = ArgumentCaptor.forClass(ApexSessionParams.class); @@ -544,9 +543,8 @@ public class StagingManagerTest { when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos); // Call and verify - Map<String, ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession); - assertThat(result).containsExactly(fakeApexInfos[0].moduleName, fakeApexInfos[0], - fakeApexInfos[1].moduleName, fakeApexInfos[1]); + List<ApexInfo> result = mStagingManager.getStagedApexInfos(parentSession); + assertThat(result).containsExactly(fakeApexInfos[0], fakeApexInfos[1]); ArgumentCaptor<ApexSessionParams> argumentCaptor = ArgumentCaptor.forClass(ApexSessionParams.class); @@ -557,7 +555,7 @@ public class StagingManagerTest { } @Test - public void getStagedApexModuleNames_returnsStagedApexModules() throws Exception { + public void getStagedApexInfos_returnsStagedApexModules() throws Exception { FakeStagedSession validSession1 = new FakeStagedSession(239); validSession1.setIsApex(true); validSession1.setSessionReady(); @@ -575,8 +573,8 @@ public class StagingManagerTest { mockApexManagerGetStagedApexInfoWithSessionId(); - List<String> result = mStagingManager.getStagedApexModuleNames(); - assertThat(result).containsExactly("239", "123", "124"); + List<StagedApexInfo> result = mStagingManager.getStagedApexInfos(); + assertThat(result).containsExactly((Object[]) fakeStagedApexInfos("239", "123", "124")); verify(mApexManager, times(2)).getStagedApexInfos(any()); } @@ -605,26 +603,12 @@ public class StagingManagerTest { }); } - @Test - public void getStagedApexInfo() throws Exception { - FakeStagedSession validSession1 = new FakeStagedSession(239); - validSession1.setIsApex(true); - validSession1.setSessionReady(); - mStagingManager.createSession(validSession1); - ApexInfo[] fakeApexInfos = getFakeApexInfo(Arrays.asList("module1")); - when(mApexManager.getStagedApexInfos(any())).thenReturn(fakeApexInfos); - - // Verify null is returned if module name is not found - StagedApexInfo result = mStagingManager.getStagedApexInfo("not found"); - assertThat(result).isNull(); - verify(mApexManager, times(1)).getStagedApexInfos(any()); - // Otherwise, the correct object is returned - result = mStagingManager.getStagedApexInfo("module1"); - assertThat(result.moduleName).isEqualTo(fakeApexInfos[0].moduleName); - assertThat(result.diskImagePath).isEqualTo(fakeApexInfos[0].modulePath); - assertThat(result.versionCode).isEqualTo(fakeApexInfos[0].versionCode); - assertThat(result.versionName).isEqualTo(fakeApexInfos[0].versionName); - verify(mApexManager, times(2)).getStagedApexInfos(any()); + private StagedApexInfo[] fakeStagedApexInfos(String... moduleNames) { + return Arrays.stream(moduleNames).map(moduleName -> { + StagedApexInfo info = new StagedApexInfo(); + info.moduleName = moduleName; + return info; + }).toArray(StagedApexInfo[]::new); } @Test @@ -646,8 +630,8 @@ public class StagingManagerTest { ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass( ApexStagedEvent.class); verify(observer, times(1)).onApexStaged(argumentCaptor.capture()); - assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo( - new String[]{"239"}); + assertThat(argumentCaptor.getValue().stagedApexInfos).isEqualTo( + fakeStagedApexInfos("239")); } // Create another staged session and verify observers are notified of union @@ -662,8 +646,8 @@ public class StagingManagerTest { ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass( ApexStagedEvent.class); verify(observer, times(1)).onApexStaged(argumentCaptor.capture()); - assertThat(argumentCaptor.getValue().stagedApexModuleNames).isEqualTo( - new String[]{"239", "240"}); + assertThat(argumentCaptor.getValue().stagedApexInfos).isEqualTo( + fakeStagedApexInfos("239", "240")); } // Finally, verify that once unregistered, observer is not notified @@ -699,7 +683,7 @@ public class StagingManagerTest { ArgumentCaptor<ApexStagedEvent> argumentCaptor = ArgumentCaptor.forClass( ApexStagedEvent.class); verify(observer, times(1)).onApexStaged(argumentCaptor.capture()); - assertThat(argumentCaptor.getValue().stagedApexModuleNames).hasLength(0); + assertThat(argumentCaptor.getValue().stagedApexInfos).hasLength(0); } @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java index 9983fb4748a5..c099517475c2 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -506,6 +506,7 @@ public class WallpaperManagerServiceTests { } @Test + @Ignore("b/372942682") public void testWallpaperManagerCallbackInRightOrder() throws RemoteException { WallpaperData wallpaper = new WallpaperData(USER_SYSTEM, FLAG_SYSTEM); wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.RED), diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index a1db18232c09..1c7fc63efd41 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -54,6 +54,7 @@ import android.os.test.TestLooper; import android.provider.Settings; import android.testing.TestableContext; import android.util.IntArray; +import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.Display; import android.view.DisplayAddress; @@ -384,6 +385,32 @@ public class NotifierTest { } @Test + public void testOnGroupRemoved_perDisplayWakeByTouchEnabled() { + createNotifier(); + // GIVEN per-display wake by touch is enabled and one display group has been defined + when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(true); + final int groupId = 313; + final int displayId1 = 3113; + final int displayId2 = 4114; + final int[] displays = new int[]{displayId1, displayId2}; + when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displays)); + when(mDisplayManagerInternal.getDisplayIdsForGroup(groupId)).thenReturn(displays); + mNotifier.onGroupWakefulnessChangeStarted( + groupId, WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_TAP, /* eventTime= */ 1000); + final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray(); + expectedDisplayInteractivities.put(displayId1, true); + expectedDisplayInteractivities.put(displayId2, true); + verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities); + + // WHEN display group is removed + when(mDisplayManagerInternal.getDisplayIdsByGroupsIds()).thenReturn(new SparseArray<>()); + mNotifier.onGroupRemoved(groupId); + + // THEN native input manager is informed that displays in that group no longer exist + verify(mInputManagerInternal).setDisplayInteractivities(new SparseBooleanArray()); + } + + @Test public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException { when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); createNotifier(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java index c037f97e34c9..2408cc1d0b3d 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsImplTest.java @@ -54,6 +54,8 @@ import android.os.HandlerThread; import android.os.Parcel; import android.os.WakeLockStats; import android.os.WorkSource; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.ravenwood.RavenwoodRule; import android.util.SparseArray; import android.view.Display; @@ -66,6 +68,7 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader import com.android.internal.os.KernelSingleUidTimeReader; import com.android.internal.os.LongArrayMultiStateCounter; import com.android.internal.os.PowerProfile; +import com.android.server.power.feature.flags.Flags; import com.google.common.collect.ImmutableList; import com.google.common.truth.LongSubject; @@ -86,11 +89,16 @@ import java.util.List; @RunWith(AndroidJUnit4.class) @SuppressWarnings("GuardedBy") public class BatteryStatsImplTest { - @Rule + @Rule(order = 0) public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() .setProvideMainThread(true) + .setSystemPropertyImmutable("persist.sys.com.android.server.power.feature.flags." + + "framework_wakelock_info-override", null) .build(); + @Rule(order = 1) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock private KernelCpuUidFreqTimeReader mKernelUidCpuFreqTimeReader; @Mock @@ -572,6 +580,7 @@ public class BatteryStatsImplTest { } @Test + @EnableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) public void testGetWakeLockStats() { mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java index 2ccb6420bc43..69579d6cb7d8 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsNoteTest.java @@ -91,9 +91,14 @@ import java.util.function.IntConsumer; public class BatteryStatsNoteTest { @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() - .setProvideMainThread(true) - .build(); + public final RavenwoodRule mRavenwood = + new RavenwoodRule.Builder() + .setProvideMainThread(true) + .setSystemPropertyImmutable( + "persist.sys.com.android.server.power.feature.flags." + + "framework_wakelock_info-override", + null) + .build(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -192,6 +197,7 @@ public class BatteryStatsNoteTest { * Test BatteryStatsImpl.Uid.noteStartWakeLocked. */ @Test + @EnableFlags(com.android.server.power.feature.flags.Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) public void testNoteStartWakeLocked() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); @@ -222,6 +228,7 @@ public class BatteryStatsNoteTest { * Test BatteryStatsImpl.Uid.noteStartWakeLocked for an isolated uid. */ @Test + @EnableFlags(com.android.server.power.feature.flags.Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) public void testNoteStartWakeLocked_isolatedUid() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms PowerStatsUidResolver uidResolver = new PowerStatsUidResolver(); @@ -264,6 +271,7 @@ public class BatteryStatsNoteTest { * isolated uid is removed from batterystats before the wakelock has been stopped. */ @Test + @EnableFlags(com.android.server.power.feature.flags.Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) public void testNoteStartWakeLocked_isolatedUidRace() throws Exception { final MockClock clocks = new MockClock(); // holds realtime and uptime in ms PowerStatsUidResolver uidResolver = new PowerStatsUidResolver(); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java index 5b7762d7de65..9645e9088bbb 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerCalculatorTest.java @@ -23,12 +23,15 @@ import android.os.BatteryStats; import android.os.Process; import android.os.UidBatteryConsumer; import android.os.WorkSource; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.PowerProfile; +import com.android.server.power.feature.flags.Flags; import org.junit.Rule; import org.junit.Test; @@ -38,20 +41,29 @@ import org.junit.runner.RunWith; @SmallTest public class WakelockPowerCalculatorTest { @Rule(order = 0) - public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() - .setProvideMainThread(true) - .build(); + public final RavenwoodRule mRavenwood = + new RavenwoodRule.Builder() + .setProvideMainThread(true) + .setSystemPropertyImmutable( + "persist.sys.com.android.server.power.feature.flags." + + "framework_wakelock_info-override", + null) + .build(); + + @Rule(order = 1) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private static final double PRECISION = 0.00001; private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; private static final int APP_PID = 3145; - @Rule(order = 1) - public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() - .setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0); + @Rule(order = 2) + public final BatteryUsageStatsRule mStatsRule = + new BatteryUsageStatsRule().setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0); @Test + @EnableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) public void testTimerBasedModel() { mStatsRule.getUidStats(Process.ROOT_UID); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java index dd5df76ef83f..cef3fddc6aba 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java @@ -24,10 +24,14 @@ import static org.mockito.Mockito.mock; import android.content.Context; import android.os.Process; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.DisabledOnRavenwood; +import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.ravenwood.RavenwoodConfig; import android.platform.test.ravenwood.RavenwoodConfig.Config; import com.android.internal.os.PowerStats; +import com.android.server.power.feature.flags.Flags; import com.android.server.power.stats.format.WakelockPowerStatsLayout; import org.junit.Before; @@ -37,11 +41,19 @@ import org.junit.Test; public class WakelockPowerStatsCollectorTest { @Config - public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setProvideMainThread(true) - .build(); - - @Rule + public static final RavenwoodConfig sConfig = + new RavenwoodConfig.Builder() + .setProvideMainThread(true) + .setSystemPropertyImmutable( + "persist.sys.com.android.server.power.feature.flags." + + "framework_wakelock_info-override", + null) + .build(); + + @Rule(order = 0) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Rule(order = 1) public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(); private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42; @@ -63,6 +75,8 @@ public class WakelockPowerStatsCollectorTest { } @Test + @DisableFlags(Flags.FLAG_FRAMEWORK_WAKELOCK_INFO) + @DisabledOnRavenwood(reason = "b/372292543 temporary disable") public void collectStats() { PowerStatsCollector powerStatsCollector = mBatteryStats.getPowerStatsCollector( POWER_COMPONENT_WAKELOCK); diff --git a/services/tests/security/forensic/OWNERS b/services/tests/security/forensic/OWNERS new file mode 100644 index 000000000000..80c9afb96033 --- /dev/null +++ b/services/tests/security/forensic/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 36824 + +file:platform/frameworks/base:main:/core/java/android/security/forensic/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java index 3e2949d60183..de5564cb7704 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java @@ -20,6 +20,8 @@ import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX; import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME; import static com.android.media.audio.Flags.absVolumeIndexFix; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -109,12 +111,13 @@ public class AudioDeviceVolumeManagerTest { mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName); mTestLooper.dispatchAll(); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - AudioManager.STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE); + eq(AudioManager.STREAM_MUSIC), eq(minIndex), anyBoolean(), + eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName); mTestLooper.dispatchAll(); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE); + AudioManager.STREAM_MUSIC, midIndex, false, AudioSystem.DEVICE_OUT_USB_DEVICE); } @Test @@ -151,7 +154,7 @@ public class AudioDeviceVolumeManagerTest { // Stream volume changes verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - AudioManager.STREAM_MUSIC, targetIndex, + AudioManager.STREAM_MUSIC, targetIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET); } @@ -162,7 +165,7 @@ public class AudioDeviceVolumeManagerTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - AudioManager.STREAM_MUSIC, maxIndex, + AudioManager.STREAM_MUSIC, maxIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET); } @@ -193,8 +196,8 @@ public class AudioDeviceVolumeManagerTest { } // Stream volume changes verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - AudioManager.STREAM_MUSIC, passedIndex, - AudioSystem.DEVICE_OUT_BLE_HEADSET); + AudioManager.STREAM_MUSIC, passedIndex, false, + AudioSystem.DEVICE_OUT_BLE_HEADSET); } // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4) @@ -207,7 +210,7 @@ public class AudioDeviceVolumeManagerTest { passedIndex = 4; } verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - AudioManager.STREAM_MUSIC, passedIndex, - AudioSystem.DEVICE_OUT_BLE_HEADSET); + AudioManager.STREAM_MUSIC, passedIndex, false, + AudioSystem.DEVICE_OUT_BLE_HEADSET); } } diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java index 96ac5d251ffd..ce59a86c6ca3 100644 --- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java +++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java @@ -132,12 +132,13 @@ public class NoOpAudioSystemAdapter extends AudioSystemAdapter { } @Override - public int setStreamVolumeIndexAS(int stream, int index, int device) { + public int setStreamVolumeIndexAS(int stream, int index, boolean muted, int device) { return AudioSystem.AUDIO_STATUS_OK; } @Override - public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) { + public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, boolean muted, + int device) { return AudioSystem.AUDIO_STATUS_OK; } diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java index dc8c1b9c8a10..6b41c434b80f 100644 --- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java @@ -18,6 +18,7 @@ package com.android.server.audio; import static android.media.AudioManager.ADJUST_LOWER; import static android.media.AudioManager.ADJUST_MUTE; import static android.media.AudioManager.ADJUST_RAISE; +import static android.media.AudioManager.ADJUST_UNMUTE; import static android.media.AudioManager.DEVICE_OUT_BLE_SPEAKER; import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO; import static android.media.AudioManager.DEVICE_OUT_SPEAKER; @@ -41,13 +42,13 @@ import static android.view.KeyEvent.KEYCODE_VOLUME_UP; import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX; import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME; +import static com.android.media.audio.Flags.FLAG_RING_MY_CAR; import static com.android.media.audio.Flags.absVolumeIndexFix; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; @@ -180,6 +181,10 @@ public class VolumeHelperTest { } return mStreamDevice.get(stream); } + + public void setMuteAffectedStreams(int muteAffectedStreams) { + mMuteAffectedStreams = muteAffectedStreams; + } } private static class TestDeviceVolumeBehaviorDispatcherStub @@ -223,6 +228,7 @@ public class VolumeHelperTest { mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer, mMockPermissionProvider); + mAudioService.setMuteAffectedStreams(AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED); mTestLooper.dispatchAll(); prepareAudioServiceState(); @@ -258,6 +264,8 @@ public class VolumeHelperTest { for (int streamType : usedStreamTypes) { mAudioService.setStreamVolume(streamType, DEFAULT_STREAM_VOLUME, /*flags=*/0, mContext.getOpPackageName()); + mAudioService.adjustStreamVolume(streamType, ADJUST_UNMUTE, /*flags=*/0, + mContext.getOpPackageName()); } if (!mIsAutomotive) { @@ -301,7 +309,20 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem).setStreamVolumeIndexAS( - eq(STREAM_MUSIC), eq(newIndex), eq(DEVICE_OUT_USB_DEVICE)); + STREAM_MUSIC, newIndex, /*muted=*/false, DEVICE_OUT_USB_DEVICE); + } + + @Test + @RequiresFlagsEnabled(FLAG_RING_MY_CAR) + public void adjustStreamVolume_adjustMute_callsASSetStreamVolumeIndex() throws Exception { + int currentIndex = mAudioService.getStreamVolume(STREAM_MUSIC); + + mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_MUTE, /*flags=*/0, + mContext.getOpPackageName()); + mTestLooper.dispatchAll(); + + verify(mSpyAudioSystem).setStreamVolumeIndexAS( + eq(STREAM_MUSIC), eq(currentIndex), /*muted=*/eq(true), anyInt()); } @Test @@ -325,7 +346,7 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE)); + eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(DEVICE_OUT_USB_DEVICE)); } @Test @@ -341,7 +362,7 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem).setStreamVolumeIndexAS( - eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE)); + eq(STREAM_MUSIC), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE)); } // --------------- Volume Group APIs --------------- @@ -356,15 +377,15 @@ public class VolumeHelperTest { mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE); mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(), circularNoMinMaxIncrementVolume(STREAM_MUSIC), /*flags=*/0, - mContext.getOpPackageName(), /*attributionTag*/null); + mContext.getOpPackageName(), /*attributionTag*/null); mTestLooper.dispatchAll(); - verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(), + verify(mSpyAudioSystem).setVolumeIndexForAttributes(any(), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE)); } @Test - public void adjustVolumeGroupVolume_callsASSetVolumeIndexForAttributes() throws Exception { + public void adjustVolumeGroupVolume_callsASSetStreamVolumeIndexAS() throws Exception { assumeNotNull(mAudioMusicVolumeGroup); mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE); @@ -372,8 +393,24 @@ public class VolumeHelperTest { ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName()); mTestLooper.dispatchAll(); - verify(mSpyAudioSystem).setVolumeIndexForAttributes( - any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE)); + // adjust calls setStreamVolumeIndexAS instead of setVolumeIndexForAttributes + verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( + anyInt(), anyInt(), anyBoolean(), eq(DEVICE_OUT_USB_DEVICE)); + } + + @Test + @RequiresFlagsEnabled(FLAG_RING_MY_CAR) + public void adjustVolumeGroupVolume_adjustMute_callsASSetStreamVolumeIndexAS() + throws Exception { + assumeNotNull(mAudioMusicVolumeGroup); + + mAudioService.adjustVolumeGroupVolume(mAudioMusicVolumeGroup.getId(), + ADJUST_MUTE, /*flags=*/0, mContext.getOpPackageName()); + mTestLooper.dispatchAll(); + + // adjust calls setStreamVolumeIndexAS instead of setVolumeIndexForAttributes + verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( + anyInt(), anyInt(), eq(true), anyInt()); } @Test @@ -437,7 +474,7 @@ public class VolumeHelperTest { @Test public void check_isStreamAffectedByMute() { - assertFalse(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL)); + assertTrue(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL)); } // --------------------- Volume Flag Check -------------------- @@ -452,14 +489,14 @@ public class VolumeHelperTest { mContext.getOpPackageName()); mTestLooper.dispatchAll(); verify(mSpyAudioSystem).setStreamVolumeIndexAS( - eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER)); + eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER)); reset(mSpyAudioSystem); mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER, FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName()); mTestLooper.dispatchAll(); verify(mSpyAudioSystem).setStreamVolumeIndexAS( - eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER)); + eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER)); } @Test @@ -471,13 +508,13 @@ public class VolumeHelperTest { mContext.getOpPackageName()); mTestLooper.dispatchAll(); verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS( - eq(STREAM_NOTIFICATION), eq(newIndex), eq(DEVICE_OUT_BLE_SPEAKER)); + eq(STREAM_NOTIFICATION), eq(newIndex), eq(false), eq(DEVICE_OUT_BLE_SPEAKER)); mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER, FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName()); mTestLooper.dispatchAll(); verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS( - eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER)); + eq(STREAM_NOTIFICATION), anyInt(), eq(false), eq(DEVICE_OUT_BLE_SPEAKER)); } @Test @@ -523,7 +560,7 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS( - eq(STREAM_MUSIC), anyInt(), anyInt()); + eq(STREAM_MUSIC), anyInt(), eq(false), anyInt()); } @Test @@ -537,7 +574,7 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS( - eq(STREAM_VOICE_CALL), anyInt(), eq(DEVICE_OUT_USB_DEVICE)); + eq(STREAM_VOICE_CALL), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE)); mAudioService.setDeviceForStream(STREAM_BLUETOOTH_SCO, DEVICE_OUT_BLUETOOTH_SCO); mAudioService.adjustStreamVolume(STREAM_BLUETOOTH_SCO, ADJUST_MUTE, /*flags=*/0, @@ -545,7 +582,7 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS( - eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(DEVICE_OUT_USB_DEVICE)); + eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(false), eq(DEVICE_OUT_USB_DEVICE)); } // ----------------- AudioDeviceVolumeManager ----------------- @@ -568,18 +605,18 @@ public class VolumeHelperTest { mTestLooper.dispatchAll(); // there is a min/max index mismatch in automotive - assertEquals(volMin, mAudioService.getDeviceVolume(volMin, usbDevice, - mContext.getOpPackageName())); + assertEquals(volMin.getVolumeIndex(), mAudioService.getDeviceVolume(volMin, usbDevice, + mContext.getOpPackageName()).getVolumeIndex()); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); + eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName()); mTestLooper.dispatchAll(); // there is a min/max index mismatch in automotive - assertEquals(volMid, mAudioService.getDeviceVolume(volMid, usbDevice, - mContext.getOpPackageName())); + assertEquals(volMid.getVolumeIndex(), mAudioService.getDeviceVolume(volMid, usbDevice, + mContext.getOpPackageName()).getVolumeIndex()); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - eq(STREAM_MUSIC), anyInt(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); + eq(STREAM_MUSIC), anyInt(), anyBoolean(), eq(AudioSystem.DEVICE_OUT_USB_DEVICE)); } @Test @@ -617,8 +654,7 @@ public class VolumeHelperTest { mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName())); // Stream volume changes verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - STREAM_MUSIC, targetIndex, - AudioSystem.DEVICE_OUT_BLE_HEADSET); + STREAM_MUSIC, targetIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET); } // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4) @@ -630,8 +666,7 @@ public class VolumeHelperTest { assertEquals(volIndex4, mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName())); verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - STREAM_MUSIC, maxIndex, - AudioSystem.DEVICE_OUT_BLE_HEADSET); + STREAM_MUSIC, maxIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET); } @Test @@ -660,8 +695,7 @@ public class VolumeHelperTest { } // Stream volume changes verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - STREAM_MUSIC, passedIndex, - AudioSystem.DEVICE_OUT_BLE_HEADSET); + STREAM_MUSIC, passedIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET); } // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4) @@ -674,8 +708,7 @@ public class VolumeHelperTest { passedIndex = 4; } verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS( - STREAM_MUSIC, passedIndex, - AudioSystem.DEVICE_OUT_BLE_HEADSET); + STREAM_MUSIC, passedIndex, false, AudioSystem.DEVICE_OUT_BLE_HEADSET); } // ---------------- DeviceVolumeBehaviorTest ---------------- diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index eb9cce007b77..d1f6c2f9f1f0 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -69,7 +69,6 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.R; import com.android.internal.pm.parsing.PackageParser2; import com.android.server.compat.PlatformCompat; -import com.android.server.integrity.engine.RuleEvaluationEngine; import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.pm.parsing.TestPackageParser2; import com.android.server.testutils.TestUtils; @@ -138,7 +137,6 @@ public class AppIntegrityManagerServiceImplTest { @Mock PlatformCompat mPlatformCompat; @Mock Context mMockContext; @Mock Resources mMockResources; - @Mock RuleEvaluationEngine mRuleEvaluationEngine; @Mock IntegrityFileManager mIntegrityFileManager; @Mock Handler mHandler; @@ -176,7 +174,6 @@ public class AppIntegrityManagerServiceImplTest { mMockContext, mPackageManagerInternal, mParserSupplier, - mRuleEvaluationEngine, mIntegrityFileManager, mHandler); @@ -307,91 +304,6 @@ public class AppIntegrityManagerServiceImplTest { } @Test - public void handleBroadcast_correctArgs() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext) - .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - Intent intent = makeVerificationIntent(); - when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); - - broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); - runJobInHandler(); - - ArgumentCaptor<AppInstallMetadata> metadataCaptor = - ArgumentCaptor.forClass(AppInstallMetadata.class); - verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); - AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); - assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName()); - assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT); - assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName()); - // we cannot check installer cert because it seems to be device specific. - assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode()); - assertFalse(appInstallMetadata.isPreInstalled()); - // Asserting source stamp not present. - assertFalse(appInstallMetadata.isStampPresent()); - assertFalse(appInstallMetadata.isStampVerified()); - assertFalse(appInstallMetadata.isStampTrusted()); - assertNull(appInstallMetadata.getStampCertificateHash()); - // These are hardcoded in the test apk android manifest - Map<String, String> allowedInstallers = - appInstallMetadata.getAllowedInstallersAndCertificates(); - assertEquals(2, allowedInstallers.size()); - assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG)); - assertEquals(INSTALLER_CERTIFICATE_NOT_EVALUATED, allowedInstallers.get(ADB_INSTALLER)); - } - - @Test - public void handleBroadcast_correctArgs_multipleCerts() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext) - .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - Intent intent = makeVerificationIntent(); - intent.setDataAndType(Uri.fromFile(mTestApkTwoCerts), PACKAGE_MIME_TYPE); - when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); - - broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); - runJobInHandler(); - - ArgumentCaptor<AppInstallMetadata> metadataCaptor = - ArgumentCaptor.forClass(AppInstallMetadata.class); - verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); - AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); - assertThat(appInstallMetadata.getAppCertificates()) - .containsExactly(DUMMY_APP_TWO_CERTS_CERT_1, DUMMY_APP_TWO_CERTS_CERT_2); - } - - @Test - public void handleBroadcast_correctArgs_sourceStamp() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext) - .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - Intent intent = makeVerificationIntent(); - intent.setDataAndType(Uri.fromFile(mTestApkSourceStamp), PACKAGE_MIME_TYPE); - when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); - - broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); - runJobInHandler(); - - ArgumentCaptor<AppInstallMetadata> metadataCaptor = - ArgumentCaptor.forClass(AppInstallMetadata.class); - verify(mRuleEvaluationEngine).evaluate(metadataCaptor.capture()); - AppInstallMetadata appInstallMetadata = metadataCaptor.getValue(); - assertTrue(appInstallMetadata.isStampPresent()); - assertTrue(appInstallMetadata.isStampVerified()); - assertTrue(appInstallMetadata.isStampTrusted()); - assertEquals(SOURCE_STAMP_CERTIFICATE_HASH, appInstallMetadata.getStampCertificateHash()); - } - - @Test public void handleBroadcast_allow() throws Exception { allowlistUsAsRuleProvider(); makeUsSystemApp(); @@ -400,7 +312,6 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(); - when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); runJobInHandler(); @@ -411,32 +322,6 @@ public class AppIntegrityManagerServiceImplTest { } @Test - public void handleBroadcast_reject() throws Exception { - allowlistUsAsRuleProvider(); - makeUsSystemApp(); - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mMockContext) - .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); - when(mRuleEvaluationEngine.evaluate(any())) - .thenReturn( - IntegrityCheckResult.deny( - Arrays.asList( - new Rule( - new AtomicFormula.BooleanAtomicFormula( - AtomicFormula.PRE_INSTALLED, false), - Rule.DENY)))); - Intent intent = makeVerificationIntent(); - - broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); - runJobInHandler(); - - verify(mPackageManagerInternal) - .setIntegrityVerificationResult( - 1, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT); - } - - @Test public void handleBroadcast_notInitialized() throws Exception { allowlistUsAsRuleProvider(); makeUsSystemApp(); @@ -446,7 +331,6 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(); - when(mRuleEvaluationEngine.evaluate(any())).thenReturn(IntegrityCheckResult.allow()); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); runJobInHandler(); @@ -467,8 +351,6 @@ public class AppIntegrityManagerServiceImplTest { verify(mMockContext, atLeastOnce()) .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any()); Intent intent = makeVerificationIntent(TEST_FRAMEWORK_PACKAGE); - when(mRuleEvaluationEngine.evaluate(any())) - .thenReturn(IntegrityCheckResult.deny(/* rule= */ null)); broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent); runJobInHandler(); diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java deleted file mode 100644 index 1c860ca31990..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java +++ /dev/null @@ -1,192 +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.server.integrity.engine; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.IntegrityFormula; -import android.content.integrity.Rule; - -import com.android.server.integrity.IntegrityFileManager; -import com.android.server.integrity.model.IntegrityCheckResult; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -@RunWith(JUnit4.class) -public class RuleEvaluationEngineTest { - - private static final String INSTALLER_1 = "installer1"; - private static final String INSTALLER_1_CERT = "installer1_cert"; - private static final String INSTALLER_2 = "installer2"; - private static final String INSTALLER_2_CERT = "installer2_cert"; - - private static final String RANDOM_INSTALLER = "random"; - private static final String RANDOM_INSTALLER_CERT = "random_cert"; - - @Mock - private IntegrityFileManager mIntegrityFileManager; - - private RuleEvaluationEngine mEngine; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mEngine = new RuleEvaluationEngine(mIntegrityFileManager); - - when(mIntegrityFileManager.readRules(any())).thenReturn(Collections.singletonList(new Rule( - IntegrityFormula.Installer.notAllowedByManifest(), Rule.DENY))); - - when(mIntegrityFileManager.initialized()).thenReturn(true); - } - - @Test - public void testAllowedInstallers_empty() { - AppInstallMetadata appInstallMetadata1 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .build(); - AppInstallMetadata appInstallMetadata2 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_2) - .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT)) - .build(); - AppInstallMetadata appInstallMetadata3 = - getAppInstallMetadataBuilder() - .setInstallerName(RANDOM_INSTALLER) - .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT)) - .build(); - - assertThat(mEngine.evaluate(appInstallMetadata1).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - assertThat(mEngine.evaluate(appInstallMetadata2).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - assertThat(mEngine.evaluate(appInstallMetadata3).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - } - - @Test - public void testAllowedInstallers_oneElement() { - Map<String, String> allowedInstallers = - Collections.singletonMap(INSTALLER_1, INSTALLER_1_CERT); - - AppInstallMetadata appInstallMetadata1 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .setAllowedInstallersAndCert(allowedInstallers) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata1).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - - AppInstallMetadata appInstallMetadata2 = - getAppInstallMetadataBuilder() - .setInstallerName(RANDOM_INSTALLER) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata2).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.DENY); - - AppInstallMetadata appInstallMetadata3 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata3).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.DENY); - - AppInstallMetadata appInstallMetadata4 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata4).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.DENY); - } - - @Test - public void testAllowedInstallers_multipleElement() { - Map<String, String> allowedInstallers = new HashMap<>(2); - allowedInstallers.put(INSTALLER_1, INSTALLER_1_CERT); - allowedInstallers.put(INSTALLER_2, INSTALLER_2_CERT); - - AppInstallMetadata appInstallMetadata1 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata1).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - - AppInstallMetadata appInstallMetadata2 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_2) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata2).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.ALLOW); - - AppInstallMetadata appInstallMetadata3 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_1) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata3).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.DENY); - - AppInstallMetadata appInstallMetadata4 = - getAppInstallMetadataBuilder() - .setInstallerName(INSTALLER_2) - .setAllowedInstallersAndCert(allowedInstallers) - .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT)) - .build(); - assertThat(mEngine.evaluate(appInstallMetadata4).getEffect()) - .isEqualTo(IntegrityCheckResult.Effect.DENY); - } - - /** Returns a builder with all fields filled with some placeholder data. */ - private AppInstallMetadata.Builder getAppInstallMetadataBuilder() { - return new AppInstallMetadata.Builder() - .setPackageName("abc") - .setAppCertificates(Collections.singletonList("abc")) - .setAppCertificateLineage(Collections.singletonList("abc")) - .setInstallerCertificates(Collections.singletonList("abc")) - .setInstallerName("abc") - .setVersionCode(-1) - .setIsPreInstalled(true); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java deleted file mode 100644 index 5089f74894d9..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java +++ /dev/null @@ -1,299 +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.server.integrity.engine; - -import static com.android.server.integrity.model.IntegrityCheckResult.Effect.ALLOW; -import static com.android.server.integrity.model.IntegrityCheckResult.Effect.DENY; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.AtomicFormula; -import android.content.integrity.AtomicFormula.LongAtomicFormula; -import android.content.integrity.AtomicFormula.StringAtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.Rule; - -import com.android.server.integrity.model.IntegrityCheckResult; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@RunWith(JUnit4.class) -public class RuleEvaluatorTest { - - private static final String PACKAGE_NAME_1 = "com.test.app"; - private static final String PACKAGE_NAME_2 = "com.test.app2"; - private static final String APP_CERTIFICATE = "test_cert"; - private static final AppInstallMetadata APP_INSTALL_METADATA = - new AppInstallMetadata.Builder() - .setPackageName(PACKAGE_NAME_1) - .setAppCertificates(Collections.singletonList(APP_CERTIFICATE)) - .setAppCertificateLineage(Collections.singletonList(APP_CERTIFICATE)) - .setVersionCode(2) - .build(); - - @Test - public void testEvaluateRules_noRules_allow() { - List<Rule> rules = new ArrayList<>(); - - IntegrityCheckResult result = RuleEvaluator.evaluateRules(rules, APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(ALLOW); - } - - @Test - public void testEvaluateRules_noMatchedRules_allow() { - Rule rule = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_2, - /* isHashedValue= */ false), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(ALLOW); - } - - @Test - public void testEvaluateRules_oneMatch_deny() { - Rule rule1 = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - Rule.DENY); - Rule rule2 = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_2, - /* isHashedValue= */ false), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule1); - } - - @Test - public void testEvaluateRules_multipleMatches_deny() { - Rule rule1 = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - Rule.DENY); - Rule rule2 = new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - APP_CERTIFICATE, - /* isHashedValue= */ false))), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule1, rule2); - } - - @Test - public void testEvaluateRules_ruleWithNot_deny() { - Rule rule = new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_2, - /* isHashedValue= */ false))), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule); - } - - @Test - public void testEvaluateRules_ruleWithIntegerOperators_deny() { - Rule rule = - new Rule( - new LongAtomicFormula(AtomicFormula.VERSION_CODE, - AtomicFormula.GT, 1), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule); - } - - @Test - public void testEvaluateRules_validForm_deny() { - Rule rule = new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - APP_CERTIFICATE, - /* isHashedValue= */ false))), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule); - } - - @Test - public void testEvaluateRules_orRules() { - Rule rule = new Rule( - new CompoundFormula( - CompoundFormula.OR, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - APP_CERTIFICATE, - /* isHashedValue= */ false))), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule); - } - - @Test - public void testEvaluateRules_compoundFormulaWithNot_deny() { - CompoundFormula openSubFormula = - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_2, - /* isHashedValue= */ false), - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - APP_CERTIFICATE, - /* isHashedValue= */ false))); - CompoundFormula compoundFormula = - new CompoundFormula(CompoundFormula.NOT, Collections.singletonList(openSubFormula)); - Rule rule = new Rule(compoundFormula, Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Collections.singletonList(rule), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(DENY); - assertThat(result.getMatchedRules()).containsExactly(rule); - } - - @Test - public void testEvaluateRules_forceAllow() { - Rule rule1 = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - Rule.FORCE_ALLOW); - Rule rule2 = new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - APP_CERTIFICATE, - /* isHashedValue= */ false))), - Rule.DENY); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(ALLOW); - assertThat(result.getMatchedRules()).containsExactly(rule1); - } - - @Test - public void testEvaluateRules_multipleMatches_forceAllow() { - Rule rule1 = - new Rule( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - Rule.FORCE_ALLOW); - Rule rule2 = new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - PACKAGE_NAME_1, - /* isHashedValue= */ false), - new StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - APP_CERTIFICATE, - /* isHashedValue= */ false))), - Rule.FORCE_ALLOW); - - IntegrityCheckResult result = - RuleEvaluator.evaluateRules(Arrays.asList(rule1, rule2), APP_INSTALL_METADATA); - - assertThat(result.getEffect()).isEqualTo(ALLOW); - assertThat(result.getMatchedRules()).containsExactly(rule1, rule2); - } -}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java index 6c23ff6fcb3c..d31ed689a7da 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java @@ -22,8 +22,6 @@ import android.content.integrity.AtomicFormula; import android.content.integrity.CompoundFormula; import android.content.integrity.Rule; -import com.android.internal.util.FrameworkStatsLog; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -40,8 +38,6 @@ public class IntegrityCheckResultTest { assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW); assertThat(allowResult.getMatchedRules()).isEmpty(); - assertThat(allowResult.getLoggingResponse()) - .isEqualTo(FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__ALLOWED); } @Test @@ -58,9 +54,6 @@ public class IntegrityCheckResultTest { assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW); assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule); - assertThat(allowResult.getLoggingResponse()) - .isEqualTo( - FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__FORCE_ALLOWED); } @Test @@ -77,8 +70,6 @@ public class IntegrityCheckResultTest { assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY); assertThat(denyResult.getMatchedRules()).containsExactly(failedRule); - assertThat(denyResult.getLoggingResponse()) - .isEqualTo(FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED__RESPONSE__REJECTED); } @Test diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index bf58443194e5..a222ef04ac30 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -175,8 +175,7 @@ public class TunerResourceManagerServiceTest { tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); - Map<Integer, FrontendResource> resources = - mTunerResourceManagerService.getFrontendResources(); + Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); for (int id = 0; id < infos.length; id++) { assertThat(resources.get(infos[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); @@ -203,15 +202,14 @@ public class TunerResourceManagerServiceTest { tunerFrontendInfo(3 /*handle*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); - Map<Integer, FrontendResource> resources = - mTunerResourceManagerService.getFrontendResources(); + Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE) .containsExactlyElementsIn(Arrays.asList(infos)); - assertThat(resources.get(0).getExclusiveGroupMemberFeHandles()).isEmpty(); - assertThat(resources.get(1).getExclusiveGroupMemberFeHandles()).containsExactly(2, 3); - assertThat(resources.get(2).getExclusiveGroupMemberFeHandles()).containsExactly(1, 3); - assertThat(resources.get(3).getExclusiveGroupMemberFeHandles()).containsExactly(1, 2); + assertThat(resources.get(0L).getExclusiveGroupMemberFeHandles()).isEmpty(); + assertThat(resources.get(1L).getExclusiveGroupMemberFeHandles()).containsExactly(2L, 3L); + assertThat(resources.get(2L).getExclusiveGroupMemberFeHandles()).containsExactly(1L, 3L); + assertThat(resources.get(3L).getExclusiveGroupMemberFeHandles()).containsExactly(1L, 2L); } @Test @@ -224,11 +222,11 @@ public class TunerResourceManagerServiceTest { tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); - Map<Integer, FrontendResource> resources0 = + Map<Long, FrontendResource> resources0 = mTunerResourceManagerService.getFrontendResources(); mTunerResourceManagerService.setFrontendInfoListInternal(infos); - Map<Integer, FrontendResource> resources1 = + Map<Long, FrontendResource> resources1 = mTunerResourceManagerService.getFrontendResources(); assertThat(resources0).isEqualTo(resources1); @@ -251,8 +249,7 @@ public class TunerResourceManagerServiceTest { tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos1); - Map<Integer, FrontendResource> resources = - mTunerResourceManagerService.getFrontendResources(); + Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); for (int id = 0; id < infos1.length; id++) { assertThat(resources.get(infos1[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); @@ -278,8 +275,7 @@ public class TunerResourceManagerServiceTest { tunerFrontendInfo(1 /*handle*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos1); - Map<Integer, FrontendResource> resources = - mTunerResourceManagerService.getFrontendResources(); + Map<Long, FrontendResource> resources = mTunerResourceManagerService.getFrontendResources(); for (int id = 0; id < infos1.length; id++) { assertThat(resources.get(infos1[id].handle) .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0); @@ -296,7 +292,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.setFrontendInfoListInternal(infos0); TunerFrontendRequest request = tunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); @@ -317,7 +313,7 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); @@ -349,7 +345,7 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(0); @@ -382,7 +378,7 @@ public class TunerResourceManagerServiceTest { 1 /*exclusiveGroupId*/); mTunerResourceManagerService.setFrontendInfoListInternal(infos); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; TunerFrontendRequest request = tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); assertThat(mTunerResourceManagerService @@ -423,7 +419,7 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); @@ -463,12 +459,12 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); assertThat(client0.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList(infos[0].handle, infos[1].handle))); + .isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle, infos[1].handle))); request = tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBS); @@ -505,7 +501,7 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); @@ -536,7 +532,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/); - int[] casSessionHandle = new int[1]; + long[] casSessionHandle = new long[1]; // Request for 2 cas sessions. assertThat(mTunerResourceManagerService .requestCasSessionInternal(request, casSessionHandle)).isTrue(); @@ -583,7 +579,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/); - int[] ciCamHandle = new int[1]; + long[] ciCamHandle = new long[1]; // Request for 2 ciCam sessions. assertThat(mTunerResourceManagerService .requestCiCamInternal(request, ciCamHandle)).isTrue(); @@ -626,7 +622,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); CasSessionRequest request = casSessionRequest(client0.getId(), 1 /*casSystemId*/); - int[] casSessionHandle = new int[1]; + long[] casSessionHandle = new long[1]; // Request for 1 cas sessions. assertThat(mTunerResourceManagerService .requestCasSessionInternal(request, casSessionHandle)).isTrue(); @@ -660,7 +656,7 @@ public class TunerResourceManagerServiceTest { mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); TunerCiCamRequest request = tunerCiCamRequest(client0.getId(), 1 /*ciCamId*/); - int[] ciCamHandle = new int[1]; + long[] ciCamHandle = new long[1]; // Request for 1 ciCam sessions. assertThat(mTunerResourceManagerService .requestCiCamInternal(request, ciCamHandle)).isTrue(); @@ -696,17 +692,17 @@ public class TunerResourceManagerServiceTest { TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 500); // Init lnb resources. - int[] lnbHandles = {1}; + long[] lnbHandles = {1}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); TunerLnbRequest request = new TunerLnbRequest(); request.clientId = client0.getId(); - int[] lnbHandle = new int[1]; + long[] lnbHandle = new long[1]; assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]); assertThat(client0.getProfile().getInUseLnbHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0]))); + .isEqualTo(new HashSet<Long>(Arrays.asList(lnbHandles[0]))); request = new TunerLnbRequest(); request.clientId = client1.getId(); @@ -732,12 +728,12 @@ public class TunerResourceManagerServiceTest { TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); // Init lnb resources. - int[] lnbHandles = {0}; + long[] lnbHandles = {0}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); TunerLnbRequest request = new TunerLnbRequest(); request.clientId = client0.getId(); - int[] lnbHandle = new int[1]; + long[] lnbHandle = new long[1]; assertThat(mTunerResourceManagerService .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]); @@ -768,7 +764,7 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = tunerFrontendRequest(client0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; assertThat(mTunerResourceManagerService .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); @@ -799,12 +795,12 @@ public class TunerResourceManagerServiceTest { infos[2] = tunerDemuxInfo(2 /* handle */, Filter.TYPE_TS); mTunerResourceManagerService.setDemuxInfoListInternal(infos); - int[] demuxHandle0 = new int[1]; + long[] demuxHandle0 = new long[1]; // first with undefined type (should be the first one with least # of caps) TunerDemuxRequest request = tunerDemuxRequest(client0.getId(), Filter.TYPE_UNDEFINED); assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0)) .isTrue(); - assertThat(demuxHandle0[0]).isEqualTo(1); + assertThat(demuxHandle0[0]).isEqualTo(1L); DemuxResource dr = mTunerResourceManagerService.getDemuxResource(demuxHandle0[0]); mTunerResourceManagerService.releaseDemuxInternal(dr); @@ -813,20 +809,20 @@ public class TunerResourceManagerServiceTest { demuxHandle0[0] = -1; assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0)) .isFalse(); - assertThat(demuxHandle0[0]).isEqualTo(-1); + assertThat(demuxHandle0[0]).isEqualTo(-1L); // now with TS (should be the one with least # of caps that supports TS) request.desiredFilterTypes = Filter.TYPE_TS; assertThat(mTunerResourceManagerService.requestDemuxInternal(request, demuxHandle0)) .isTrue(); - assertThat(demuxHandle0[0]).isEqualTo(2); + assertThat(demuxHandle0[0]).isEqualTo(2L); // request for another TS TunerClient client1 = new TunerClient(); client1.register("1" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] demuxHandle1 = new int[1]; + long[] demuxHandle1 = new long[1]; TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_TS); assertThat(mTunerResourceManagerService.requestDemuxInternal(request1, demuxHandle1)) .isTrue(); @@ -865,14 +861,14 @@ public class TunerResourceManagerServiceTest { // let client0(prio:100) request for IP - should succeed TunerDemuxRequest request0 = tunerDemuxRequest(client0.getId(), Filter.TYPE_IP); - int[] demuxHandle0 = new int[1]; + long[] demuxHandle0 = new long[1]; assertThat(mTunerResourceManagerService .requestDemuxInternal(request0, demuxHandle0)).isTrue(); assertThat(demuxHandle0[0]).isEqualTo(0); // let client1(prio:50) request for IP - should fail TunerDemuxRequest request1 = tunerDemuxRequest(client1.getId(), Filter.TYPE_IP); - int[] demuxHandle1 = new int[1]; + long[] demuxHandle1 = new long[1]; demuxHandle1[0] = -1; assertThat(mTunerResourceManagerService .requestDemuxInternal(request1, demuxHandle1)).isFalse(); @@ -892,7 +888,7 @@ public class TunerResourceManagerServiceTest { // let client2(prio:50) request for TS - should succeed TunerDemuxRequest request2 = tunerDemuxRequest(client2.getId(), Filter.TYPE_TS); - int[] demuxHandle2 = new int[1]; + long[] demuxHandle2 = new long[1]; assertThat(mTunerResourceManagerService .requestDemuxInternal(request2, demuxHandle2)).isTrue(); assertThat(demuxHandle2[0]).isEqualTo(0); @@ -917,7 +913,7 @@ public class TunerResourceManagerServiceTest { client0.register("0" /*sessionId*/, TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); - int[] desHandle = new int[1]; + long[] desHandle = new long[1]; TunerDescramblerRequest request = new TunerDescramblerRequest(); request.clientId = client0.getId(); assertThat(mTunerResourceManagerService.requestDescramblerInternal(request, desHandle)) @@ -980,7 +976,7 @@ public class TunerResourceManagerServiceTest { 1 /*exclusiveGroupId*/); /**** Init Lnb Resources ****/ - int[] lnbHandles = {1}; + long[] lnbHandles = {1}; mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles); // Update frontend list in TRM @@ -989,7 +985,7 @@ public class TunerResourceManagerServiceTest { /**** Request Frontend ****/ // Predefined frontend request and array to save returned frontend handle - int[] frontendHandle = new int[1]; + long[] frontendHandle = new long[1]; TunerFrontendRequest request = tunerFrontendRequest( ownerClient0.getId() /*clientId*/, FrontendSettings.TYPE_DVBT); @@ -1000,7 +996,7 @@ public class TunerResourceManagerServiceTest { .isTrue(); assertThat(frontendHandle[0]).isEqualTo(infos[0].handle); assertThat(ownerClient0.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); @@ -1030,15 +1026,15 @@ public class TunerResourceManagerServiceTest { shareClient1.getId()))); // Verify in use frontend list in all the primary owner and share owner clients assertThat(ownerClient0.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); assertThat(shareClient0.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); assertThat(shareClient1.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); @@ -1052,12 +1048,12 @@ public class TunerResourceManagerServiceTest { .isEqualTo(new HashSet<Integer>(Arrays.asList( shareClient0.getId()))); assertThat(ownerClient0.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); assertThat(shareClient0.getProfile() .getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); @@ -1080,7 +1076,7 @@ public class TunerResourceManagerServiceTest { assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].handle) .getOwnerClientId()).isEqualTo(ownerClient1.getId()); assertThat(ownerClient1.getProfile().getInUseFrontendHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( infos[0].handle, infos[1].handle))); assertThat(ownerClient0.getProfile().getInUseFrontendHandles() @@ -1127,7 +1123,7 @@ public class TunerResourceManagerServiceTest { // Predefined Lnb request and handle array TunerLnbRequest requestLnb = new TunerLnbRequest(); requestLnb.clientId = shareClient0.getId(); - int[] lnbHandle = new int[1]; + long[] lnbHandle = new long[1]; // Request for an Lnb assertThat(mTunerResourceManagerService @@ -1155,7 +1151,7 @@ public class TunerResourceManagerServiceTest { .isEmpty()) .isTrue(); assertThat(shareClient0.getProfile().getInUseLnbHandles()) - .isEqualTo(new HashSet<Integer>(Arrays.asList( + .isEqualTo(new HashSet<Long>(Arrays.asList( lnbHandles[0]))); ownerClient0.unregister(); @@ -1163,7 +1159,7 @@ public class TunerResourceManagerServiceTest { } private TunerFrontendInfo tunerFrontendInfo( - int handle, int frontendType, int exclusiveGroupId) { + long handle, int frontendType, int exclusiveGroupId) { TunerFrontendInfo info = new TunerFrontendInfo(); info.handle = handle; info.type = frontendType; diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index c247c08c8010..3b0cb4ad8779 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -68,6 +68,7 @@ import static org.testng.Assert.assertThrows; import android.Manifest; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.Flags; import android.app.IOnProjectionStateChangedListener; @@ -247,6 +248,8 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { mInjector = spy(new TestInjector()); mUiManagerService = new UiModeManagerService(mContext, /* setupWizardComplete= */ true, mTwilightManager, mInjector); + // Initialize the current user. + mUiManagerService.setCurrentUser(ActivityManager.getCurrentUser()); try { mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); } catch (SecurityException e) {/* ignore for permission denial */} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java index 4af96ef4800f..05210aca19dd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/EventConditionProviderTest.java @@ -19,13 +19,25 @@ package com.android.server.notification; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.app.Application; import android.app.PendingIntent; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.UserHandle; +import android.os.UserManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -35,16 +47,24 @@ import com.android.server.UiServiceTestCase; import com.android.server.pm.PackageManagerService; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.util.List; @RunWith(AndroidTestingRunner.class) @SmallTest @RunWithLooper public class EventConditionProviderTest extends UiServiceTestCase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - EventConditionProvider mService; + private EventConditionProvider mService; + @Mock private UserManager mUserManager; @Before public void setUp() throws Exception { @@ -65,6 +85,18 @@ public class EventConditionProviderTest extends UiServiceTestCase { service.onCreate(); service.onBind(startIntent); mService = spy(service); + mService.mContext = this.getContext(); + + mContext.addMockSystemService(UserManager.class, mUserManager); + when(mUserManager.getProfiles(eq(mUserId))).thenReturn( + List.of(new UserInfo(mUserId, "mUserId", 0))); + + doAnswer((Answer<Context>) invocationOnMock -> { + Context mockUserContext = mock(Context.class); + UserHandle userHandle = invocationOnMock.getArgument(2); + when(mockUserContext.getUserId()).thenReturn(userHandle.getIdentifier()); + return mockUserContext; + }).when(mContext).createPackageContextAsUser(any(), anyInt(), any()); } @Test @@ -78,4 +110,52 @@ public class EventConditionProviderTest extends UiServiceTestCase { PendingIntent pi = mService.getPendingIntent(1000); assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage()); } + + @Test + @DisableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onBootComplete_flagOff_loadsTrackers() { + when(mUserManager.getUserProfiles()).thenReturn(List.of(UserHandle.of(mUserId))); + + mService.onBootComplete(); + + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(mUserId); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onBootComplete_waitsForUserSwitched() { + mService.onBootComplete(); + assertThat(mService.getTrackers().size()).isEqualTo(0); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onUserSwitched_reloadsTrackers() { + UserHandle someUser = UserHandle.of(42); + when(mUserManager.getProfiles(eq(42))).thenReturn(List.of(new UserInfo(42, "user 42", 0))); + + mService.onUserSwitched(someUser); + + assertThat(mService.getTrackers().size()).isEqualTo(1); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42); + assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(42); + } + + @Test + @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) + public void onUserSwitched_reloadsTrackersIncludingProfiles() { + UserHandle anotherUser = UserHandle.of(42); + when(mUserManager.getProfiles(eq(42))).thenReturn(List.of( + new UserInfo(42, "user 42", 0), + new UserInfo(43, "profile 43", 0))); + + mService.onUserSwitched(anotherUser); + + assertThat(mService.getTrackers().size()).isEqualTo(2); + assertThat(mService.getTrackers().keyAt(0)).isEqualTo(42); + assertThat(mService.getTrackers().valueAt(0).getUserId()).isEqualTo(42); + assertThat(mService.getTrackers().keyAt(1)).isEqualTo(43); + assertThat(mService.getTrackers().valueAt(1).getUserId()).isEqualTo(43); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index 797b95b5dbe9..0f7de7d78ccf 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -16,6 +16,9 @@ package com.android.server.notification; import static android.os.UserHandle.USER_ALL; +import static android.service.notification.Adjustment.KEY_IMPORTANCE; + +import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS; import static com.google.common.truth.Truth.assertThat; @@ -145,7 +148,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { mContext.setMockPackageManager(mPm); mContext.addMockSystemService(Context.USER_SERVICE, mUm); mContext.getOrCreateTestableResources().addOverride( - com.android.internal.R.string.config_defaultAssistantAccessComponent, "a/a"); + com.android.internal.R.string.config_defaultAssistantAccessComponent, + mCn.flattenToString()); mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm)); when(mNm.getBinderService()).thenReturn(mINm); mContext.ensureTestableResources(); @@ -207,7 +211,8 @@ public class NotificationAssistantsTest extends UiServiceTestCase { writeXmlAndReload(USER_ALL); - ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0); + ArrayMap<Boolean, ArraySet<String>> approved = + mAssistants.mApproved.get(ActivityManager.getCurrentUser()); // approved should not be null assertNotNull(approved); assertEquals(new ArraySet<>(), approved.get(true)); @@ -217,6 +222,35 @@ public class NotificationAssistantsTest extends UiServiceTestCase { } @Test + public void testWriteXml_userTurnedOffNAS_backup() throws Exception { + int userId = 10; + + mAssistants.loadDefaultsFromConfig(true); + + mAssistants.setPackageOrComponentEnabled(mCn.flattenToString(), userId, true, + true, true); + + ComponentName current = CollectionUtils.firstOrNull( + mAssistants.getAllowedComponents(userId)); + mAssistants.setUserSet(userId, true); + mAssistants.setPackageOrComponentEnabled(current.flattenToString(), userId, true, false, + true); + assertTrue(mAssistants.mIsUserChanged.get(userId)); + assertThat(mAssistants.getApproved(userId, true)).isEmpty(); + + writeXmlAndReload(userId); + + ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(userId); + // approved should not be null + assertNotNull(approved); + assertEquals(new ArraySet<>(), approved.get(true)); + + // user set is maintained + assertTrue(mAssistants.mIsUserChanged.get(userId)); + assertThat(mAssistants.getApproved(userId, true)).isEmpty(); + } + + @Test public void testReadXml_userDisabled() throws Exception { String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">" + "<service_listing approved=\"\" user=\"0\" primary=\"true\"" @@ -600,4 +634,50 @@ public class NotificationAssistantsTest extends UiServiceTestCase { assertThat(mAssistants.getUnsupportedAdjustments(userId).size()).isEqualTo(0); } + + @Test + @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void testDisallowAdjustmentType() { + mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE); + assertThat(mAssistants.getAllowedAssistantAdjustments()) + .doesNotContain(Adjustment.KEY_RANKING_SCORE); + assertThat(mAssistants.getAllowedAssistantAdjustments()).contains(Adjustment.KEY_TYPE); + } + + @Test + @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void testAllowAdjustmentType() { + mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE); + assertThat(mAssistants.getAllowedAssistantAdjustments()) + .doesNotContain(Adjustment.KEY_RANKING_SCORE); + mAssistants.allowAdjustmentType(Adjustment.KEY_RANKING_SCORE); + assertThat(mAssistants.getAllowedAssistantAdjustments()) + .contains(Adjustment.KEY_RANKING_SCORE); + } + + @Test + @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION) + public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception { + int userId = ActivityManager.getCurrentUser(); + + mAssistants.loadDefaultsFromConfig(true); + mAssistants.disallowAdjustmentType(KEY_IMPORTANCE); + + writeXmlAndReload(USER_ALL); + + assertThat(mAssistants.getAllowedAssistantAdjustments()).contains( + Adjustment.KEY_NOT_CONVERSATION); + assertThat(mAssistants.getAllowedAssistantAdjustments()).doesNotContain( + KEY_IMPORTANCE); + } + + @Test + public void testDefaultAllowedAdjustments_readWriteXml_entries() throws Exception { + mAssistants.loadDefaultsFromConfig(true); + + writeXmlAndReload(USER_ALL); + + assertThat(mAssistants.getAllowedAssistantAdjustments()) + .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENTS); + } }
\ No newline at end of file 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 1349ee04696d..e845d80b412a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -201,6 +201,7 @@ import android.app.RemoteInput; import android.app.RemoteInputHistoryItem; import android.app.StatsManager; import android.app.admin.DevicePolicyManagerInternal; +import android.app.backup.BackupRestoreEventLogger; import android.app.job.JobScheduler; import android.app.role.RoleManager; import android.app.usage.UsageStatsManagerInternal; @@ -4837,7 +4838,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, mPkg, Process.myUserHandle()); verify(mPreferencesHelper, times(1)).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4855,7 +4856,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4870,7 +4871,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, mPkg, Process.myUserHandle()); verify(mPreferencesHelper, times(1)).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4890,7 +4891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -4912,7 +4913,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } verify(mPreferencesHelper, never()).getNotificationChannels( - anyString(), anyInt(), anyBoolean()); + anyString(), anyInt(), anyBoolean(), anyBoolean()); } @Test @@ -6340,7 +6341,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false, - UserHandle.USER_ALL); + UserHandle.USER_ALL, null); verify(mListeners, times(1)).readXml(any(), any(), anyBoolean(), anyInt()); verify(mConditionProviders, times(1)).readXml(any(), any(), anyBoolean(), anyInt()); verify(mAssistants, times(1)).readXml(any(), any(), anyBoolean(), anyInt()); @@ -6360,7 +6361,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false, - UserHandle.USER_ALL); + UserHandle.USER_ALL, null); verify(mSnoozeHelper, times(1)).readXml(any(TypedXmlPullParser.class), anyLong()); } @@ -6372,7 +6373,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false, - UserHandle.USER_ALL); + UserHandle.USER_ALL, null); verify(mListeners, never()).readXml(any(), any(), anyBoolean(), anyInt()); verify(mConditionProviders, never()).readXml(any(), any(), anyBoolean(), anyInt()); verify(mAssistants, never()).readXml(any(), any(), anyBoolean(), anyInt()); @@ -6404,7 +6405,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())), true, - 10); + 10, null); verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10)); verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10)); verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10)); @@ -6430,7 +6431,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())), true, - 10); + 10, null); verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10)); verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10)); verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10)); @@ -6458,7 +6459,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())), true, - 10); + 10, null); verify(mListeners, never()).readXml(any(), any(), eq(true), eq(10)); verify(mConditionProviders, never()).readXml(any(), any(), eq(true), eq(10)); verify(mAssistants, never()).readXml(any(), any(), eq(true), eq(10)); @@ -6485,7 +6486,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.readPolicyXml( new BufferedInputStream(new ByteArrayInputStream(policyXml.getBytes())), true, - 10); + 10, null); verify(mListeners, times(1)).readXml(any(), any(), eq(true), eq(10)); verify(mConditionProviders, times(1)).readXml(any(), any(), eq(true), eq(10)); verify(mAssistants, times(1)).readXml(any(), any(), eq(true), eq(10)); @@ -17061,4 +17062,20 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(mService.hasFlag(captor.getValue().getNotification().flags, FLAG_PROMOTED_ONGOING)).isFalse(); } + + @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testAppCannotUseReservedBundleChannels() throws Exception { + mBinderService.getBubblePreferenceForPackage(mPkg, mUid); + NotificationChannel news = mBinderService.getNotificationChannel( + mPkg, mContext.getUserId(), mPkg, NEWS_ID); + assertThat(news).isNotNull(); + + NotificationRecord nr = generateNotificationRecord(news); + mBinderService.enqueueNotificationWithTag(mPkg, mPkg, nr.getSbn().getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + assertThat(mService.mNotificationList).isEmpty(); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 50a5f658f059..4391152220c0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -546,7 +546,8 @@ public class NotificationRecordTest extends UiServiceTestCase { } @Test - @EnableFlags(com.android.server.notification.Flags.FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI) + @EnableFlags(com.android.server.notification.Flags + .FLAG_NOTIFICATION_VIBRATION_IN_SOUND_URI_FOR_CHANNEL) public void testVibration_customVibrationForSound_withVibrationUri() throws IOException { defaultChannel.enableVibration(true); VibrationInfo vibration = getTestingVibration(mVibrator); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 404ede676f02..b92bdb5f3e6e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -2547,7 +2547,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // Returns only non-deleted channels List<NotificationChannel> channels = - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList(); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList(); // Default channel + non-deleted channel + system defaults assertEquals(notificationClassification() ? 6 : 2, channels.size()); for (NotificationChannel nc : channels) { @@ -2557,7 +2557,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { } // Returns deleted channels too - channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList(); + channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList(); // Includes system channel(s) assertEquals(notificationClassification() ? 7 : 3, channels.size()); for (NotificationChannel nc : channels) { @@ -3199,7 +3199,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.permanentlyDeleteNotificationChannels(PKG_N_MR1, UID_N_MR1); // Only default channel remains - assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true).getList().size()); + assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true) + .getList().size()); } @Test @@ -3315,12 +3316,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { // user 0 records remain for (int i = 0; i < user0Uids.length; i++) { assertEquals(notificationClassification() ? 5 : 1, - mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false) + mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true) .getList().size()); } // user 1 records are gone for (int i = 0; i < user1Uids.length; i++) { - assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false) + assertEquals(0, mHelper.getNotificationChannels(PKG_N_MR1, user1Uids[i], false, true) .getList().size()); } } @@ -3337,7 +3338,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { new int[]{UID_N_MR1})); assertEquals(0, mHelper.getNotificationChannels( - PKG_N_MR1, UID_N_MR1, true).getList().size()); + PKG_N_MR1, UID_N_MR1, true, true).getList().size()); // Not deleted mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false, @@ -3346,7 +3347,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{UID_N_MR1})); assertEquals(notificationClassification() ? 6 : 2, - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) + .getList().size()); } @Test @@ -3405,7 +3407,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue(mHelper.canShowBadge(PKG_O, UID_O)); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O)); - assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true).getList().size()); + assertEquals(0, mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size()); assertEquals(0, mHelper.getNotificationChannelGroups(PKG_O, UID_O).size()); NotificationChannel channel = getChannel(); @@ -3419,7 +3421,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testRecordDefaults() throws Exception { assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertEquals(notificationClassification() ? 5 : 1, - mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); + mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true) + .getList().size()); } @Test @@ -6363,6 +6366,15 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) + public void testGetNotificationChannels_omitBundleChannels() { + // do something that triggers settings creation for an app + mHelper.setShowBadge(PKG_O, UID_O, true); + + assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty(); + } + + @Test + @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION) public void testNotificationBundles() { // do something that triggers settings creation for an app mHelper.setShowBadge(PKG_O, UID_O, true); diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java index f7127df0ee33..3b2f532dddd7 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackVibrationProviderTest.java @@ -453,20 +453,7 @@ public class HapticFeedbackVibrationProviderTest { } @Test - public void testVibrationAttribute_scrollFeedback_inputCustomizedFlag_useTouchUsage() { - mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); - HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations(); - - for (int effectId : SCROLL_FEEDBACK_CONSTANTS) { - VibrationAttributes attrs = provider.getVibrationAttributes(effectId, /* flags */ - 0, /* privFlags */ 0); - assertWithMessage("Expected USAGE_TOUCH for scroll effect " + effectId - + ", if no input customization").that(attrs.getUsage()).isEqualTo(USAGE_TOUCH); - } - } - - @Test - public void testVibrationAttribute_scrollFeedback_noInputCustomizedFlag_useHardwareFeedback() { + public void testVibrationAttribute_scrollFeedback_useHardwareFeedback() { HapticFeedbackVibrationProvider provider = createProviderWithoutCustomizations(); for (int effectId : SCROLL_FEEDBACK_CONSTANTS) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index d0080d29f82b..d5f86b6feac8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1261,6 +1261,33 @@ public class ActivityStarterTests extends WindowTestsBase { } @Test + public void testLaunchAdjacentDisabled() { + final ActivityStarter starter = + prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */); + final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityOptions options = ActivityOptions.makeBasic(); + final ActivityRecord[] outActivity = new ActivityRecord[1]; + + // Activity must not land on split-screen task if currently not in split-screen mode. + starter.setActivityOptions(options.toBundle()) + .setReason("testLaunchAdjacentDisabled") + .setOutActivity(outActivity).execute(); + assertThat(outActivity[0].inMultiWindowMode()).isFalse(); + + // Move activity to split-screen-primary task and make sure it has the focus. + TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent()); + top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM); + top.getRootTask().moveToFront("testLaunchAdjacentDisabled"); + top.getRootTask().setLaunchAdjacentDisabled(true); + + // Ensure activity does not launch into split-screen-secondary when launch adjacent is + // disabled + startActivityInner(starter, outActivity[0], top, options, null /* inTask */, + null /* taskFragment*/); + assertThat(outActivity[0].isDescendantOf(splitOrg.mSecondary)).isFalse(); + } + + @Test public void testTransientLaunchWithKeyguard() { final ActivityStarter starter = prepareStarter(0 /* flags */); final ActivityRecord target = new ActivityBuilder(mAtm).setCreateTask(true).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java index 3910904337b2..750968100e2b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java @@ -683,6 +683,41 @@ public class BackgroundActivityStartControllerExemptionTests { } @Test + @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES) + public void testRealCaller_sawPermission() { + int callingUid = REGULAR_UID_1; + int callingPid = REGULAR_PID_1; + final String callingPackage = REGULAR_PACKAGE_1; + int realCallingUid = REGULAR_UID_2; + int realCallingPid = REGULAR_PID_2; + + // setup state + when(mService.hasSystemAlertWindowPermission(eq(realCallingUid), eq(realCallingPid), + any())).thenReturn(true); + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = + mCheckedOptions.setPendingIntentBackgroundActivityStartMode( + MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS); + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, null, + originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent, + checkedOptions); + + // call + BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller( + balState); + balState.setResultForCaller(callerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( + BAL_ALLOW_SAW_PERMISSION); + } + + @Test public void testCaller_isRecents() { int callingUid = REGULAR_UID_1; int callingPid = REGULAR_PID_1; diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 72f4fa9158fb..c1edae91aabb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -4883,7 +4883,7 @@ public class SizeCompatTests extends WindowTestsBase { @Test - @EnableCompatChanges({ActivityRecord.UNIVERSAL_RESIZABLE_BY_DEFAULT}) + @EnableCompatChanges({ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT}) public void testUniversalResizeableByDefault() { mSetFlagsRule.enableFlags(Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT); mDisplayContent.setIgnoreOrientationRequest(false); diff --git a/services/usb/java/com/android/server/usb/UsbManagerInternal.java b/services/usb/java/com/android/server/usb/UsbManagerInternal.java index c97df6b4f63a..31c5986c45b8 100644 --- a/services/usb/java/com/android/server/usb/UsbManagerInternal.java +++ b/services/usb/java/com/android/server/usb/UsbManagerInternal.java @@ -34,9 +34,11 @@ import java.lang.annotation.RetentionPolicy; public abstract class UsbManagerInternal { public static final int OS_USB_DISABLE_REASON_AAPM = 0; + public static final int OS_USB_DISABLE_REASON_LOCKDOWN_MODE = 1; @Retention(RetentionPolicy.SOURCE) - @IntDef(value = {OS_USB_DISABLE_REASON_AAPM}) + @IntDef(value = {OS_USB_DISABLE_REASON_AAPM, + OS_USB_DISABLE_REASON_LOCKDOWN_MODE}) public @interface OsUsbDisableReason { } diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index ba9dff656f0a..ec4f7e1ea4ba 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -1527,8 +1527,11 @@ public class UsbService extends IUsbManager.Stub { } mLockdownModeStatus = lockDownTriggeredByUser; for (UsbPort port: mPortManager.getPorts()) { - enableUsbData(port.getId(), !lockDownTriggeredByUser, STRONG_AUTH_OPERATION_ID, - new IUsbOperationInternal.Default()); + enableUsbDataInternal(port.getId(), !lockDownTriggeredByUser, + STRONG_AUTH_OPERATION_ID, + new IUsbOperationInternal.Default(), + UsbManagerInternal.OS_USB_DISABLE_REASON_LOCKDOWN_MODE, + true); } } } diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java index e2fb6019f30a..ad0d4f4de3ae 100644 --- a/telecomm/java/android/telecom/Logging/Session.java +++ b/telecomm/java/android/telecom/Logging/Session.java @@ -16,7 +16,6 @@ package android.telecom.Logging; -import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -24,8 +23,13 @@ import android.telecom.Log; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.telecom.flags.Flags; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; /** * Stores information about a thread's point of entry into that should persist until that thread @@ -55,7 +59,7 @@ public class Session { * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()} * if the Session is canceled. */ - public static final int UNDEFINED = -1; + public static final long UNDEFINED = -1; public static class Info implements Parcelable { public final String sessionId; @@ -129,46 +133,39 @@ public class Session { } } - private String mSessionId; - private String mShortMethodName; + private final String mSessionId; + private volatile String mShortMethodName; private long mExecutionStartTimeMs; private long mExecutionEndTimeMs = UNDEFINED; - private Session mParentSession; - private ArrayList<Session> mChildSessions; + private volatile Session mParentSession; + private final ArrayList<Session> mChildSessions = new ArrayList<>(5); private boolean mIsCompleted = false; - private boolean mIsExternal = false; - private int mChildCounter = 0; + private final boolean mIsExternal; + private final AtomicInteger mChildCounter = new AtomicInteger(0); // True if this is a subsession that has been started from the same thread as the parent // session. This can happen if Log.startSession(...) is called multiple times on the same // thread in the case of one Telecom entry point method calling another entry point method. // In this case, we can just make this subsession "invisible," but still keep track of it so // that the Log.endSession() calls match up. - private boolean mIsStartedFromActiveSession = false; + private final boolean mIsStartedFromActiveSession; // Optionally provided info about the method/class/component that started the session in order // to make Logging easier. This info will be provided in parentheses along with the session. - private String mOwnerInfo; + private final String mOwnerInfo; // Cache Full Method path so that recursive population of the full method path only needs to // be calculated once. - private String mFullMethodPathCache; + private volatile String mFullMethodPathCache; public Session(String sessionId, String shortMethodName, long startTimeMs, - boolean isStartedFromActiveSession, String ownerInfo) { - setSessionId(sessionId); + boolean isStartedFromActiveSession, boolean isExternal, String ownerInfo) { + mSessionId = (sessionId != null) ? sessionId : "???"; setShortMethodName(shortMethodName); mExecutionStartTimeMs = startTimeMs; mParentSession = null; - mChildSessions = new ArrayList<>(5); mIsStartedFromActiveSession = isStartedFromActiveSession; + mIsExternal = isExternal; mOwnerInfo = ownerInfo; } - public void setSessionId(@NonNull String sessionId) { - if (sessionId == null) { - mSessionId = "?"; - } - mSessionId = sessionId; - } - public String getShortMethodName() { return mShortMethodName; } @@ -180,10 +177,6 @@ public class Session { mShortMethodName = shortMethodName; } - public void setIsExternal(boolean isExternal) { - mIsExternal = isExternal; - } - public boolean isExternal() { return mIsExternal; } @@ -193,13 +186,15 @@ public class Session { } public void addChild(Session childSession) { - if (childSession != null) { + if (childSession == null) return; + synchronized (mChildSessions) { mChildSessions.add(childSession); } } public void removeChild(Session child) { - if (child != null) { + if (child == null) return; + synchronized (mChildSessions) { mChildSessions.remove(child); } } @@ -217,7 +212,9 @@ public class Session { } public ArrayList<Session> getChildSessions() { - return mChildSessions; + synchronized (mChildSessions) { + return new ArrayList<>(mChildSessions); + } } public boolean isSessionCompleted() { @@ -259,17 +256,41 @@ public class Session { return mExecutionEndTimeMs - mExecutionStartTimeMs; } - public synchronized String getNextChildId() { - return String.valueOf(mChildCounter++); + public String getNextChildId() { + return String.valueOf(mChildCounter.getAndIncrement()); } - // Builds full session id recursively + // Builds full session ID, which incliudes the optional external indicators (E), + // base session ID, and the optional sub-session IDs (_X): @[E-]...[ID][_X][_Y]... private String getFullSessionId() { - return getFullSessionId(0); + if (!Flags.endSessionImprovements()) return getFullSessionIdRecursive(0); + int currParentCount = 0; + StringBuilder id = new StringBuilder(); + Session currSession = this; + while (currSession != null) { + Session parentSession = currSession.getParentSession(); + if (parentSession != null) { + if (currParentCount >= SESSION_RECURSION_LIMIT) { + id.insert(0, getSessionId()); + id.insert(0, TRUNCATE_STRING); + android.util.Slog.w(LOG_TAG, "getFullSessionId: Hit iteration limit!"); + return id.toString(); + } + if (Log.VERBOSE) { + id.insert(0, currSession.getSessionId()); + id.insert(0, SESSION_SEPARATION_CHAR_CHILD); + } + } else { + id.insert(0, currSession.getSessionId()); + } + currSession = parentSession; + currParentCount++; + } + return id.toString(); } // keep track of calls and bail if we hit the recursion limit - private String getFullSessionId(int parentCount) { + private String getFullSessionIdRecursive(int parentCount) { if (parentCount >= SESSION_RECURSION_LIMIT) { // Don't use Telecom's Log.w here or it will cause infinite recursion because it will // try to add session information to this logging statement, which will cause it to hit @@ -286,12 +307,12 @@ public class Session { return mSessionId; } else { if (Log.VERBOSE) { - return parentSession.getFullSessionId(parentCount + 1) + return parentSession.getFullSessionIdRecursive(parentCount + 1) // Append "_X" to subsession to show subsession designation. + SESSION_SEPARATION_CHAR_CHILD + mSessionId; } else { // Only worry about the base ID at the top of the tree. - return parentSession.getFullSessionId(parentCount + 1); + return parentSession.getFullSessionIdRecursive(parentCount + 1); } } @@ -300,16 +321,18 @@ public class Session { private Session getRootSession(String callingMethod) { int currParentCount = 0; Session topNode = this; - while (topNode.getParentSession() != null) { + Session parentNode = topNode.getParentSession(); + while (parentNode != null) { if (currParentCount >= SESSION_RECURSION_LIMIT) { // Don't use Telecom's Log.w here or it will cause infinite recursion because it // will try to add session information to this logging statement, which will cause // it to hit this condition again and so on... - android.util.Slog.w(LOG_TAG, "getRootSession: Hit recursion limit from " + android.util.Slog.w(LOG_TAG, "getRootSession: Hit iteration limit from " + callingMethod); break; } - topNode = topNode.getParentSession(); + topNode = parentNode; + parentNode = topNode.getParentSession(); currParentCount++; } return topNode; @@ -320,14 +343,40 @@ public class Session { return getRootSession("printFullSessionTree").printSessionTree(); } - // Recursively move down session tree using DFS, but print out each node when it is reached. private String printSessionTree() { StringBuilder sb = new StringBuilder(); - printSessionTree(0, sb, 0); + if (!Flags.endSessionImprovements()) { + printSessionTreeRecursive(0, sb, 0); + return sb.toString(); + } + int depth = 0; + ArrayDeque<Session> deque = new ArrayDeque<>(); + deque.add(this); + while (!deque.isEmpty()) { + Session node = deque.pollFirst(); + sb.append("\t".repeat(depth)); + sb.append(node.toString()); + sb.append("\n"); + if (depth >= SESSION_RECURSION_LIMIT) { + sb.append(TRUNCATE_STRING); + depth -= 1; + continue; + } + List<Session> childSessions = node.getChildSessions().reversed(); + if (!childSessions.isEmpty()) { + depth += 1; + for (Session child : childSessions) { + deque.addFirst(child); + } + } else { + depth -= 1; + } + } return sb.toString(); } - private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) { + // Recursively move down session tree using DFS, but print out each node when it is reached. + private void printSessionTreeRecursive(int tabI, StringBuilder sb, int currChildCount) { // Prevent infinite recursion. if (currChildCount >= SESSION_RECURSION_LIMIT) { // Don't use Telecom's Log.w here or it will cause infinite recursion because it will @@ -343,26 +392,85 @@ public class Session { for (int i = 0; i <= tabI; i++) { sb.append("\t"); } - child.printSessionTree(tabI + 1, sb, currChildCount + 1); + child.printSessionTreeRecursive(tabI + 1, sb, currChildCount + 1); } } - // Recursively concatenate mShortMethodName with the parent Sessions to create full method - // path. if truncatePath is set to true, all other external sessions (except for the most - // recent) will be truncated to "..." + // + + /** + * Concatenate the short method name with the parent Sessions to create full method path. + * @param truncatePath if truncatePath is set to true, all other external sessions (except for + * the most recent) will be truncated to "..." + * @return The full method path associated with this Session. + */ + @VisibleForTesting public String getFullMethodPath(boolean truncatePath) { StringBuilder sb = new StringBuilder(); - getFullMethodPath(sb, truncatePath, 0); + if (!Flags.endSessionImprovements()) { + getFullMethodPathRecursive(sb, truncatePath, 0); + return sb.toString(); + } + // Check to see if the session has been renamed yet. If it has not, then the session + // has not been continued. + Session parentSession = getParentSession(); + boolean isSessionStarted = parentSession == null + || !getShortMethodName().equals(parentSession.getShortMethodName()); + int depth = 0; + Session currSession = this; + while (currSession != null) { + String cache = currSession.mFullMethodPathCache; + // Return cached value for method path. When returning the truncated path, recalculate + // the full path without using the cached value. + if (!TextUtils.isEmpty(cache) && !truncatePath) { + sb.insert(0, cache); + return sb.toString(); + } + + parentSession = currSession.getParentSession(); + // Encapsulate the external session's method name so it is obvious what part of the + // session is external or truncate it if we do not want the entire history. + if (currSession.isExternal()) { + if (truncatePath) { + sb.insert(0, TRUNCATE_STRING); + } else { + sb.insert(0, ")"); + sb.insert(0, currSession.getShortMethodName()); + sb.insert(0, "("); + } + } else { + sb.insert(0, currSession.getShortMethodName()); + } + if (parentSession != null) { + sb.insert(0, SUBSESSION_SEPARATION_CHAR); + } + + if (depth >= SESSION_RECURSION_LIMIT) { + // Don't use Telecom's Log.w here or it will cause infinite recursion because it + // will try to add session information to this logging statement, which will cause + // it to hit this condition again and so on... + android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit iteration limit!"); + sb.insert(0, TRUNCATE_STRING); + return sb.toString(); + } + currSession = parentSession; + depth++; + } + if (isSessionStarted && !truncatePath) { + // Cache the full method path for this node so that we do not need to calculate it + // again in the future. + mFullMethodPathCache = sb.toString(); + } return sb.toString(); } - private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath, + private synchronized void getFullMethodPathRecursive(StringBuilder sb, boolean truncatePath, int parentCount) { if (parentCount >= SESSION_RECURSION_LIMIT) { // Don't use Telecom's Log.w here or it will cause infinite recursion because it will // try to add session information to this logging statement, which will cause it to hit // this condition again and so on... - android.util.Slog.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!"); + android.util.Slog.w(LOG_TAG, "getFullMethodPathRecursive: Hit recursion limit!"); sb.append(TRUNCATE_STRING); return; } @@ -378,7 +486,7 @@ public class Session { // Check to see if the session has been renamed yet. If it has not, then the session // has not been continued. isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName); - parentSession.getFullMethodPath(sb, truncatePath, parentCount + 1); + parentSession.getFullMethodPathRecursive(sb, truncatePath, parentCount + 1); sb.append(SUBSESSION_SEPARATION_CHAR); } // Encapsulate the external session's method name so it is obvious what part of the session @@ -409,14 +517,14 @@ public class Session { @Override public int hashCode() { - int result = mSessionId != null ? mSessionId.hashCode() : 0; - result = 31 * result + (mShortMethodName != null ? mShortMethodName.hashCode() : 0); - result = 31 * result + (int) (mExecutionStartTimeMs ^ (mExecutionStartTimeMs >>> 32)); - result = 31 * result + (int) (mExecutionEndTimeMs ^ (mExecutionEndTimeMs >>> 32)); + int result = mSessionId.hashCode(); + result = 31 * result + mShortMethodName.hashCode(); + result = 31 * result + Long.hashCode(mExecutionStartTimeMs); + result = 31 * result + Long.hashCode(mExecutionEndTimeMs); result = 31 * result + (mParentSession != null ? mParentSession.hashCode() : 0); - result = 31 * result + (mChildSessions != null ? mChildSessions.hashCode() : 0); + result = 31 * result + mChildSessions.hashCode(); result = 31 * result + (mIsCompleted ? 1 : 0); - result = 31 * result + mChildCounter; + result = 31 * result + mChildCounter.hashCode(); result = 31 * result + (mIsStartedFromActiveSession ? 1 : 0); result = 31 * result + (mOwnerInfo != null ? mOwnerInfo.hashCode() : 0); return result; @@ -432,23 +540,13 @@ public class Session { if (mExecutionStartTimeMs != session.mExecutionStartTimeMs) return false; if (mExecutionEndTimeMs != session.mExecutionEndTimeMs) return false; if (mIsCompleted != session.mIsCompleted) return false; - if (mChildCounter != session.mChildCounter) return false; + if (!(mChildCounter.get() == session.mChildCounter.get())) return false; if (mIsStartedFromActiveSession != session.mIsStartedFromActiveSession) return false; - if (mSessionId != null ? - !mSessionId.equals(session.mSessionId) : session.mSessionId != null) - return false; - if (mShortMethodName != null ? !mShortMethodName.equals(session.mShortMethodName) - : session.mShortMethodName != null) - return false; - if (mParentSession != null ? !mParentSession.equals(session.mParentSession) - : session.mParentSession != null) - return false; - if (mChildSessions != null ? !mChildSessions.equals(session.mChildSessions) - : session.mChildSessions != null) - return false; - return mOwnerInfo != null ? mOwnerInfo.equals(session.mOwnerInfo) - : session.mOwnerInfo == null; - + if (!Objects.equals(mSessionId, session.mSessionId)) return false; + if (!Objects.equals(mShortMethodName, session.mShortMethodName)) return false; + if (!Objects.equals(mParentSession, session.mParentSession)) return false; + if (!Objects.equals(mChildSessions, session.mChildSessions)) return false; + return Objects.equals(mOwnerInfo, session.mOwnerInfo); } @Override diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java index 9d17219c1ae4..00e344c67cc5 100644 --- a/telecomm/java/android/telecom/Logging/SessionManager.java +++ b/telecomm/java/android/telecom/Logging/SessionManager.java @@ -27,6 +27,7 @@ import android.telecom.Log; import android.util.Base64; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.telecom.flags.Flags; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -36,10 +37,16 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** - * TODO: Create better Sessions Documentation + * SessionManager manages the active sessions in a HashMap, which maps the active thread(s) to the + * associated {@link Session}s. + * <p> + * Note: Sessions assume that session structure modification is synchronized on this object - only + * one thread can modify the structure of any Session at one time. Printing the current session to + * the log is not synchronized because we should not clean up a session chain while printing from + * another Thread. Either the Session chain is still active and can not be cleaned up yet, or the + * Session chain has ended and we are cleaning up. * @hide */ - public class SessionManager { // Currently using 3 letters, So don't exceed 64^3 @@ -54,11 +61,11 @@ public class SessionManager { private Context mContext; @VisibleForTesting - public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100); + public final ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(64); @VisibleForTesting public java.lang.Runnable mCleanStaleSessions = () -> cleanupStaleSessions(getSessionCleanupTimeoutMs()); - private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper()); + private final Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper()); // Overridden in LogTest to skip query to ContentProvider private interface ISessionCleanupTimeoutMs { @@ -83,7 +90,7 @@ public class SessionManager { }; // Usage is synchronized on this class. - private List<ISessionListener> mSessionListeners = new ArrayList<>(); + private final List<ISessionListener> mSessionListeners = new ArrayList<>(); public interface ISessionListener { /** @@ -110,10 +117,19 @@ public class SessionManager { } private synchronized void resetStaleSessionTimer() { - mSessionCleanupHandler.removeCallbacksAndMessages(null); - // Will be null in Log Testing - if (mCleanStaleSessions != null) { - mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs()); + if (!Flags.endSessionImprovements()) { + mSessionCleanupHandler.removeCallbacksAndMessages(null); + // Will be null in Log Testing + if (mCleanStaleSessions != null) { + mSessionCleanupHandler.postDelayed(mCleanStaleSessions, + getSessionCleanupTimeoutMs()); + } + } else { + if (mCleanStaleSessions != null + && !mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) { + mSessionCleanupHandler.postDelayed(mCleanStaleSessions, + getSessionCleanupTimeoutMs()); + } } } @@ -147,13 +163,11 @@ public class SessionManager { Session childSession = createSubsession(true); continueSession(childSession, shortMethodName); return; - } else { - // Only Log that we are starting the parent session. - Log.d(LOGGING_TAG, Session.START_SESSION); } Session newSession = new Session(getNextSessionID(), shortMethodName, - System.currentTimeMillis(), false, callerIdentification); + System.currentTimeMillis(), false, false, callerIdentification); mSessionMapper.put(threadId, newSession); + Log.d(LOGGING_TAG, Session.START_SESSION); } /** @@ -179,17 +193,16 @@ public class SessionManager { } // Create Session from Info and add to the sessionMapper under this ID. - Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION); Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId, - sessionInfo.methodPath, System.currentTimeMillis(), - false /*isStartedFromActiveSession*/, sessionInfo.ownerInfo); - externalSession.setIsExternal(true); + sessionInfo.methodPath, System.currentTimeMillis(), false, true, + sessionInfo.ownerInfo); // Mark the external session as already completed, since we have no way of knowing when // the external session actually has completed. externalSession.markSessionCompleted(Session.UNDEFINED); // Track the external session with the SessionMapper so that we can create and continue // an active subsession based on it. mSessionMapper.put(threadId, externalSession); + Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION); // Create a subsession from this external Session parent node Session childSession = createSubsession(); continueSession(childSession, shortMethodName); @@ -226,13 +239,12 @@ public class SessionManager { // Start execution time of the session will be overwritten in continueSession(...). Session newSubsession = new Session(threadSession.getNextChildId(), threadSession.getShortMethodName(), System.currentTimeMillis(), - isStartedFromActiveSession, threadSession.getOwnerInfo()); + isStartedFromActiveSession, false, threadSession.getOwnerInfo()); threadSession.addChild(newSubsession); newSubsession.setParentSession(threadSession); if (!isStartedFromActiveSession) { - Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " + - newSubsession.toString()); + Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION); } else { Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " (Invisible subsession)"); @@ -273,7 +285,7 @@ public class SessionManager { } subsession.markSessionCompleted(Session.UNDEFINED); - endParentSessions(subsession); + cleanupSessionTreeAndNotify(subsession); } /** @@ -328,7 +340,7 @@ public class SessionManager { // Remove after completed so that reference still exists for logging the end events Session parentSession = completedSession.getParentSession(); mSessionMapper.remove(threadId); - endParentSessions(completedSession); + cleanupSessionTreeAndNotify(completedSession); // If this subsession was started from a parent session using Log.startSession, return the // ThreadID back to the parent after completion. if (parentSession != null && !parentSession.isSessionCompleted() && @@ -337,8 +349,49 @@ public class SessionManager { } } + /** + * Move up the session tree and remove completed sessions until we either hit a session that was + * not completed yet or we reach the root node. Once we reach the root node, we will report the + * session times to session complete listeners. + * @param session The Session to clean up. + */ + private void cleanupSessionTreeAndNotify(Session session) { + if (session == null) return; + if (!Flags.endSessionImprovements()) { + endParentSessionsRecursive(session); + return; + } + Session currSession = session; + // Traverse upwards and unlink until we either hit the root node or a node that isn't + // complete yet. + while (currSession != null) { + if (!currSession.isSessionCompleted() || !currSession.getChildSessions().isEmpty()) { + // We will return once the active session is completed. + return; + } + Session parentSession = currSession.getParentSession(); + currSession.setParentSession(null); + // The session is either complete when we have reached the top node or we have reached + // the node where the parent is external. We only want to report the time it took to + // complete the local session, so for external nodes, report finished when the sub-node + // completes. + boolean reportSessionComplete = + (parentSession == null && !currSession.isExternal()) + || (parentSession != null && parentSession.isExternal()); + if (parentSession != null) parentSession.removeChild(currSession); + if (reportSessionComplete) { + long fullSessionTimeMs = System.currentTimeMillis() + - currSession.getExecutionStartTimeMilliseconds(); + Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs + + " ms): " + currSession); + notifySessionCompleteListeners(currSession.getShortMethodName(), fullSessionTimeMs); + } + currSession = parentSession; + } + } + // Recursively deletes all complete parent sessions of the current subsession if it is a leaf. - private void endParentSessions(Session subsession) { + private void endParentSessionsRecursive(Session subsession) { // Session is not completed or not currently a leaf, so we can not remove because a child is // still running if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) { @@ -355,7 +408,7 @@ public class SessionManager { System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds(); notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs); } - endParentSessions(parentSession); + endParentSessionsRecursive(parentSession); } else { // All of the subsessions have been completed and it is time to report on the full // running time of the session. @@ -370,8 +423,10 @@ public class SessionManager { } private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) { - for (ISessionListener l : mSessionListeners) { - l.sessionComplete(methodName, sessionTimeMs); + synchronized (mSessionListeners) { + for (ISessionListener l : mSessionListeners) { + l.sessionComplete(methodName, sessionTimeMs); + } } } @@ -380,8 +435,8 @@ public class SessionManager { return currentSession != null ? currentSession.toString() : ""; } - public synchronized void registerSessionListener(ISessionListener l) { - if (l != null) { + public void registerSessionListener(ISessionListener l) { + synchronized (mSessionListeners) { mSessionListeners.add(l); } } @@ -425,25 +480,30 @@ public class SessionManager { @VisibleForTesting public synchronized void cleanupStaleSessions(long timeoutMs) { - String logMessage = "Stale Sessions Cleaned:\n"; + StringBuilder logMessage = new StringBuilder("Stale Sessions Cleaned:"); boolean isSessionsStale = false; long currentTimeMs = System.currentTimeMillis(); // Remove references that are in the Session Mapper (causing GC to occur) on - // sessions that are lasting longer than LOGGING_SESSION_TIMEOUT_MS. + // sessions that are lasting longer than DEFAULT_SESSION_TIMEOUT_MS. // If this occurs, then there is most likely a Session active that never had // Log.endSession called on it. for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it = mSessionMapper.entrySet().iterator(); it.hasNext(); ) { ConcurrentHashMap.Entry<Integer, Session> entry = it.next(); Session session = entry.getValue(); - if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) { + long runTime = currentTimeMs - session.getExecutionStartTimeMilliseconds(); + if (runTime > timeoutMs) { it.remove(); - logMessage += session.printFullSessionTree() + "\n"; + logMessage.append("\n"); + logMessage.append("["); + logMessage.append(runTime); + logMessage.append("ms] "); + logMessage.append(session.printFullSessionTree()); isSessionsStale = true; } } if (isSessionsStale) { - Log.w(LOGGING_TAG, logMessage); + Log.w(LOGGING_TAG, logMessage.toString()); } else { Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned..."); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 47f6764dba98..02999c81250a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -253,7 +253,6 @@ public class CarrierConfigManager { * * The default value is true. */ - @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU) public static final String KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL = "additional_settings_caller_id_visibility_bool"; @@ -263,7 +262,6 @@ public class CarrierConfigManager { * * The default value is true. */ - @FlaggedApi(Flags.FLAG_SHOW_CALL_ID_AND_CALL_WAITING_IN_ADDITIONAL_SETTINGS_MENU) public static final String KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL = "additional_settings_call_waiting_visibility_bool"; @@ -10558,7 +10556,6 @@ public class CarrierConfigManager { * @see SubscriptionInfo#getServiceCapabilities() * @see SubscriptionManager.OnSubscriptionsChangedListener */ - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public static final String KEY_CELLULAR_SERVICE_CAPABILITIES_INT_ARRAY = "cellular_service_capabilities_int_array"; /** @@ -11110,7 +11107,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_5G_ICON_DISPLAY_SECONDARY_GRACE_PERIOD_STRING, ""); sDefaults.putInt(KEY_NR_ADVANCED_BANDS_SECONDARY_TIMER_SECONDS_INT, 0); sDefaults.putBoolean(KEY_NR_TIMERS_RESET_IF_NON_ENDC_AND_RRC_IDLE_BOOL, false); - sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, true); + sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_VOICE_QOS_BOOL, false); sDefaults.putBoolean(KEY_NR_TIMERS_RESET_ON_PLMN_CHANGE_BOOL, false); /* Default value is 1 hour. */ sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000); diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 845449ec556a..02030a1fbaa7 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -19,6 +19,7 @@ package android.telephony; import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; import android.Manifest; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -32,6 +33,7 @@ import android.os.Binder; import android.os.Build; import android.text.TextUtils; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; import com.android.internal.telephony.Sms7BitEncodingTranslator; @@ -1208,9 +1210,9 @@ public class SmsMessage { /** * Returns the recipient address(receiver) of this SMS message in String form or null if * unavailable. - * {@hide} */ @Nullable + @FlaggedApi(Flags.FLAG_SUPPORT_SMS_OVER_IMS_APIS) public String getRecipientAddress() { return mWrappedSmsMessage.getRecipientAddress(); } diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 1089602934fd..d164c8851f5b 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -951,7 +951,6 @@ public class SubscriptionInfo implements Parcelable { * @see SubscriptionManager#SERVICE_CAPABILITY_DATA */ @NonNull - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public @SubscriptionManager.ServiceCapability Set<Integer> getServiceCapabilities() { return SubscriptionManager.getServiceCapabilitiesSet(mServiceCapabilities); } @@ -1829,7 +1828,6 @@ public class SubscriptionInfo implements Parcelable { * @throws IllegalArgumentException when any capability is not supported. */ @NonNull - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public Builder setServiceCapabilities( @NonNull @SubscriptionManager.ServiceCapability Set<Integer> capabilities) { int combinedCapabilities = 0; diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 6faef7ecfa1b..377e5f2e7db2 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1422,7 +1422,6 @@ public class SubscriptionManager { * * @see TelephonyManager#isDeviceVoiceCapable() */ - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public static final int SERVICE_CAPABILITY_VOICE = 1; /** @@ -1440,13 +1439,11 @@ public class SubscriptionManager { * * @see TelephonyManager#isDeviceSmsCapable() */ - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public static final int SERVICE_CAPABILITY_SMS = 2; /** * Represents a value indicating the data calling capabilities of a subscription. */ - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public static final int SERVICE_CAPABILITY_DATA = 3; /** @@ -3474,14 +3471,62 @@ public class SubscriptionManager { @SystemApi public boolean canManageSubscription(@NonNull SubscriptionInfo info, @NonNull String packageName) { + if (Flags.hsumPackageManager()) { + return canManageSubscriptionAsUser(info, packageName, mContext.getUser()); + } else { + if (info == null || info.getAccessRules() == null || packageName == null) { + return false; + } + PackageManager packageManager = mContext.getPackageManager(); + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNING_CERTIFICATES); + } catch (PackageManager.NameNotFoundException e) { + logd("Unknown package: " + packageName); + return false; + } + for (UiccAccessRule rule : info.getAccessRules()) { + if (rule.getCarrierPrivilegeStatus(packageInfo) + == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { + return true; + } + } + return false; + } + } + + /** + * Checks whether the given app is authorized to manage the given subscription for given user. + * + * <p>An app can only be authorized if it is available to the given user and included in the + * {@link android.telephony.UiccAccessRule} of the {@link android.telephony.SubscriptionInfo} + * with the access status. + * + * <p>Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded} returns + * true). To check for permissions for non-embedded subscription as well, + * see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. + * + * @param info The subscription to check. + * @param packageName Package name of the app to check. + * @param user UserHandle to check + * @return whether the app is authorized to manage this subscription per its access rules. + * + * @see android.telephony.TelephonyManager#hasCarrierPrivileges + * @hide + */ + public boolean canManageSubscriptionAsUser(@NonNull SubscriptionInfo info, + @NonNull String packageName, @NonNull UserHandle user) { if (info == null || info.getAccessRules() == null || packageName == null) { return false; } - PackageManager packageManager = mContext.getPackageManager(); + PackageManager pm = mContext.getUser().equals(user) + ? mContext.getPackageManager() + : mContext.createContextAsUser(user, 0).getPackageManager(); PackageInfo packageInfo; try { - packageInfo = packageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNING_CERTIFICATES); + packageInfo = pm.getPackageInfo(packageName, + PackageManager.GET_SIGNING_CERTIFICATES); } catch (PackageManager.NameNotFoundException e) { logd("Unknown package: " + packageName); return false; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f01cfc16868d..a7fe0cb0940c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2127,7 +2127,6 @@ public class TelephonyManager { * <p>On some devices, this settings activity may not exist. Callers should ensure that this * case is appropriately handled. */ - @FlaggedApi(Flags.FLAG_RESET_MOBILE_NETWORK_SETTINGS) @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_RESET_MOBILE_NETWORK_SETTINGS = "android.telephony.action.RESET_MOBILE_NETWORK_SETTINGS"; @@ -6934,7 +6933,6 @@ public class TelephonyManager { * * @see SubscriptionInfo#getServiceCapabilities() */ - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public boolean isDeviceVoiceCapable() { return isVoiceCapable(); } @@ -6974,7 +6972,6 @@ public class TelephonyManager { * * @see SubscriptionInfo#getServiceCapabilities() */ - @FlaggedApi(Flags.FLAG_DATA_ONLY_CELLULAR_SERVICE) public boolean isDeviceSmsCapable() { return isSmsCapable(); } @@ -8781,13 +8778,14 @@ public class TelephonyManager { * Authentication error, no memory space available in EFMUK * * @throws UnsupportedOperationException If the device does not have - * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given + * authType. */ // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since // it's not public API. @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) - public String getIccAuthentication(int appType,@AuthType int authType, String data) { + public String getIccAuthentication(int appType, @AuthType int authType, String data) { return getIccAuthentication(getSubId(), appType, authType, data); } @@ -8812,10 +8810,14 @@ public class TelephonyManager { * Key freshness failure * Authentication error, no memory space available * Authentication error, no memory space available in EFMUK + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given + * authType. * @hide */ @UnsupportedAppUsage - public String getIccAuthentication(int subId, int appType,@AuthType int authType, String data) { + public String getIccAuthentication(int subId, int appType, @AuthType int authType, + String data) { try { IPhoneSubInfo info = getSubscriberInfoService(); if (info == null) @@ -19180,15 +19182,19 @@ public class TelephonyManager { public @interface EmergencyCallbackModeType {} /** - * The callback mode is due to emergency call. + * The emergency callback mode is due to emergency call. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int EMERGENCY_CALLBACK_MODE_CALL = 1; /** - * The callback mode is due to emergency SMS. + * The emergency callback mode is due to emergency SMS. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int EMERGENCY_CALLBACK_MODE_SMS = 2; /** @@ -19209,45 +19215,64 @@ public class TelephonyManager { public @interface EmergencyCallbackModeStopReason {} /** - * unknown reason. + * Indicates that emergency callback mode has been stopped for an unknown reason. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_UNKNOWN = 0; /** - * The call back mode is exited due to a new normal call is originated. + * Indicates that emergency callback mode has been stopped because a new non-emergency call was + * initiated. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_OUTGOING_NORMAL_CALL_INITIATED = 1; /** - * The call back mode is exited due to a new normal SMS is originated. + * Indicates that emergency callback mode has been stopped because a new non-emergency SMS was + * sent. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_NORMAL_SMS_SENT = 2; /** - * The call back mode is exited due to a new emergency call is originated. + * Indicates that emergency callback mode has been stopped because a new outgoing emergency + * call was initiated. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED = 3; /** - * The call back mode is exited due to a new emergency SMS is originated. + * Indicates that emergency callback mode has been stopped because a new emergency SMS was sent. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_EMERGENCY_SMS_SENT = 4; /** - * The call back mode is exited due to timer expiry. + * Indicates that emergency callback mode has been stopped due to the emergency callback mode + * timer expiry. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_TIMER_EXPIRED = 5; /** - * The call back mode is exited due to user action. + * Indicates that emergency callback mode has been stopped due to user ending the emergency + * mode by clicking the notification. * @hide */ + @FlaggedApi(Flags.FLAG_EMERGENCY_CALLBACK_MODE_NOTIFICATION) + @SystemApi public static final int STOP_REASON_USER_ACTION = 6; /** diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index f2e34257ef01..9ce8e807f612 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -352,7 +352,8 @@ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity" android:theme="@style/CutoutShortEdges" android:label="SplitScreenPrimaryActivity" - android:exported="true"> + android:exported="true" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -363,7 +364,8 @@ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity" android:theme="@style/CutoutShortEdges" android:label="SplitScreenSecondaryActivity" - android:exported="true"> + android:exported="true" + android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 4ae06a4f9812..7526737f60bf 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -46,6 +46,7 @@ import com.android.internal.util.FrameworkStatsLog import com.android.modules.utils.testing.ExtendedMockitoRule import junitparams.JUnitParamsRunner import junitparams.Parameters +import org.junit.After import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -128,6 +129,13 @@ class KeyGestureControllerTests { currentPid = Process.myPid() } + @After + fun teardown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun setupBehaviors() { Mockito.`when`( resources.getBoolean( diff --git a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt index 9f4df90422eb..c61a25021949 100644 --- a/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt +++ b/tests/Input/src/com/android/test/input/UinputRecordingIntegrationTests.kt @@ -39,7 +39,6 @@ import com.android.cts.input.inputeventmatchers.withSource import junit.framework.Assert.fail import org.hamcrest.Matchers.allOf import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.rules.TestName @@ -108,7 +107,6 @@ class UinputRecordingIntegrationTests { parser = InputJsonParser(instrumentation.context) } - @Ignore("b/366602644") @Test fun testEvemuRecording() { VirtualDisplayActivityScenario.AutoClose<CaptureEventActivity>( diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java index 0375f66069c3..d9295dd17dd0 100644 --- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java @@ -515,33 +515,27 @@ public class StagedInstallInternalTest { Install.single(APEX_V2)); } - @Test - public void testGetStagedModuleNames() throws Exception { - // Before staging a session - String[] result = getPackageManagerNative().getStagedApexModuleNames(); - assertThat(result).hasLength(0); - // Stage an apex - int sessionId = Install.single(APEX_V2).setStaged().commit(); - result = getPackageManagerNative().getStagedApexModuleNames(); - assertThat(result).hasLength(1); - assertThat(result).isEqualTo(new String[]{SHIM_APEX_PACKAGE_NAME}); - // Abandon the session - InstallUtils.openPackageInstallerSession(sessionId).abandon(); - result = getPackageManagerNative().getStagedApexModuleNames(); - assertThat(result).hasLength(0); + private StagedApexInfo findStagedApexInfo(StagedApexInfo[] infos, String moduleName) { + for (StagedApexInfo info: infos) { + if (info.moduleName.equals(moduleName)) { + return info; + } + } + return null; } @Test - public void testGetStagedApexInfo() throws Exception { - // Ask for non-existing module - StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found"); - assertThat(result).isNull(); + public void testGetStagedApexInfos() throws Exception { + // Not found before staging + StagedApexInfo[] result = getPackageManagerNative().getStagedApexInfos(); + assertThat(findStagedApexInfo(result, TEST_APEX_PACKAGE_NAME)).isNull(); // Stage an apex int sessionId = Install.single(TEST_APEX_CLASSPATH).setStaged().commit(); // Query proper module name - result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME); - assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME); - assertThat(result.hasClassPathJars).isTrue(); + result = getPackageManagerNative().getStagedApexInfos(); + StagedApexInfo found = findStagedApexInfo(result, TEST_APEX_PACKAGE_NAME); + assertThat(found).isNotNull(); + assertThat(found.hasClassPathJars).isTrue(); InstallUtils.openPackageInstallerSession(sessionId).abandon(); } @@ -573,14 +567,15 @@ public class StagedInstallInternalTest { int sessionId = Install.single(APEX_V2).setStaged().commit(); ArgumentCaptor<ApexStagedEvent> captor = ArgumentCaptor.forClass(ApexStagedEvent.class); verify(observer, timeout(5000)).onApexStaged(captor.capture()); - assertThat(captor.getValue().stagedApexModuleNames).isEqualTo( - new String[] {SHIM_APEX_PACKAGE_NAME}); + StagedApexInfo found = + findStagedApexInfo(captor.getValue().stagedApexInfos, SHIM_APEX_PACKAGE_NAME); + assertThat(found).isNotNull(); // Abandon and verify observer is called Mockito.clearInvocations(observer); InstallUtils.openPackageInstallerSession(sessionId).abandon(); verify(observer, timeout(5000)).onApexStaged(captor.capture()); - assertThat(captor.getValue().stagedApexModuleNames).hasLength(0); + assertThat(captor.getValue().stagedApexInfos).hasLength(0); } @Test diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index f1fc503c8556..97abcd77e6b9 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -592,23 +592,15 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { } @Test - public void testGetStagedModuleNames() throws Exception { - assumeTrue("Device does not support updating APEX", - mHostUtils.isApexUpdateSupported()); - - runPhase("testGetStagedModuleNames"); - } - - @Test @LargeTest - public void testGetStagedApexInfo() throws Exception { + public void testGetStagedApexInfos() throws Exception { assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported()); pushTestApex(APEXD_TEST_APEX); getDevice().reboot(); - runPhase("testGetStagedApexInfo"); + runPhase("testGetStagedApexInfos"); } @Test diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 4cb7c91b2451..7e0bbc4b3e50 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -70,7 +70,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.Uri; -import android.net.vcn.Flags; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; @@ -85,7 +84,6 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.os.test.TestLooper; -import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -104,7 +102,6 @@ import com.android.server.vcn.util.PersistableBundleUtils; import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -122,8 +119,6 @@ import java.util.UUID; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagementServiceTest { - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - private static final String CONTEXT_ATTRIBUTION_TAG = "VCN"; private static final String TEST_PACKAGE_NAME = VcnManagementServiceTest.class.getPackage().getName(); @@ -285,8 +280,6 @@ public class VcnManagementServiceTest { @Before public void setUp() { - mSetFlagsRule.enableFlags(Flags.FLAG_ENFORCE_MAIN_USER); - doNothing() .when(mMockContext) .enforceCallingOrSelfPermission( diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java index edad67896e8e..421e1ad20b78 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -34,14 +34,12 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; import android.net.vcn.FeatureFlags; -import android.net.vcn.Flags; import android.os.Handler; import android.os.IPowerManager; import android.os.IThermalService; import android.os.ParcelUuid; import android.os.PowerManager; import android.os.test.TestLooper; -import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.TelephonyManager; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; @@ -49,7 +47,6 @@ import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; import org.junit.Before; -import org.junit.Rule; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -57,8 +54,6 @@ import java.util.Set; import java.util.UUID; public abstract class NetworkEvaluationTestBase { - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); - protected static final String SSID = "TestWifi"; protected static final String SSID_OTHER = "TestWifiOther"; protected static final String PLMN_ID = "123456"; @@ -120,10 +115,6 @@ public abstract class NetworkEvaluationTestBase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS); - mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE); - mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP); - when(mNetwork.getNetId()).thenReturn(-1); mTestLooper = new TestLooper(); diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp index 4920f7b41e3f..a5ff4964b0a4 100644 --- a/tools/hoststubgen/hoststubgen/Android.bp +++ b/tools/hoststubgen/hoststubgen/Android.bp @@ -8,7 +8,7 @@ package { // OWNER: g/ravenwood // Bug component: 25698 - default_team: "trendy_team_framework_backstage_power", + default_team: "trendy_team_ravenwood", } // Visibility only for ravenwood prototype uses. |