diff options
802 files changed, 14519 insertions, 4902 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java index fd8ddbcf3809..6c8af39015f5 100644 --- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java @@ -59,6 +59,10 @@ public interface JobSchedulerInternal { */ void reportAppUsage(String packageName, int userId); + /** @return {@code true} if the app is considered buggy from JobScheduler's perspective. */ + boolean isAppConsideredBuggy(int callingUserId, @NonNull String callingPackageName, + int timeoutBlameUserId, @NonNull String timeoutBlamePackageName); + /** * @return {@code true} if the given notification is associated with any user-initiated jobs. */ diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java index 8a5d09404ebb..071707059f2d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java @@ -123,21 +123,21 @@ class JobNotificationCoordinator { if (oldDetails == null) { if (jobStatus.startedAsUserInitiatedJob) { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_initial_setNotification_call_required", + "job_scheduler.value_cntr_w_uid_initial_set_notification_call_required", jobStatus.getUid()); } else { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_initial_setNotification_call_optional", + "job_scheduler.value_cntr_w_uid_initial_set_notification_call_optional", jobStatus.getUid()); } } else { if (jobStatus.startedAsUserInitiatedJob) { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_required", + "job_scheduler.value_cntr_w_uid_subsequent_set_notification_call_required", jobStatus.getUid()); } else { Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_optional", + "job_scheduler.value_cntr_w_uid_subsequent_set_notification_call_optional", jobStatus.getUid()); } if (oldDetails.notificationId != notificationId) { @@ -145,7 +145,7 @@ class JobNotificationCoordinator { removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED, jobStatus); Counter.logIncrementWithUid( - "job_scheduler.value_cntr_w_uid_setNotification_changed_notification_ids", + "job_scheduler.value_cntr_w_uid_set_notification_changed_notification_ids", jobStatus.getUid()); } } @@ -200,7 +200,10 @@ class JobNotificationCoordinator { // No more jobs using this notification. Apply the final job stop policy. // If the user attempted to stop the job/app, then always remove the notification // so the user doesn't get confused about the app state. + // Similarly, if the user background restricted the app, remove the notification so + // the user doesn't think the app is continuing to run in the background. if (details.jobEndNotificationPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE + || stopReason == JobParameters.STOP_REASON_BACKGROUND_RESTRICTION || stopReason == JobParameters.STOP_REASON_USER) { mNotificationManagerInternal.cancelNotification( packageName, packageName, details.appUid, details.appPid, /* tag */ null, diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 92fc78e273ed..f99bcf144b91 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -322,16 +322,25 @@ public class JobSchedulerService extends com.android.server.SystemService private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()"; private static final String QUOTA_TRACKER_SCHEDULE_LOGGED = ".schedulePersisted out-of-quota logged"; + private static final String QUOTA_TRACKER_TIMEOUT_UIJ_TAG = "timeout-uij"; + private static final String QUOTA_TRACKER_TIMEOUT_EJ_TAG = "timeout-ej"; + private static final String QUOTA_TRACKER_TIMEOUT_REG_TAG = "timeout-reg"; + private static final String QUOTA_TRACKER_TIMEOUT_TOTAL_TAG = "timeout-total"; + private static final String QUOTA_TRACKER_ANR_TAG = "anr"; private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category( ".schedulePersisted()"); private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category( ".schedulePersisted out-of-quota logged"); - private static final Categorizer QUOTA_CATEGORIZER = (userId, packageName, tag) -> { - if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) { - return QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED; - } - return QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED; - }; + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ = + new Category(QUOTA_TRACKER_TIMEOUT_UIJ_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ = + new Category(QUOTA_TRACKER_TIMEOUT_EJ_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_REG = + new Category(QUOTA_TRACKER_TIMEOUT_REG_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL = + new Category(QUOTA_TRACKER_TIMEOUT_TOTAL_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_ANR = new Category(QUOTA_TRACKER_ANR_TAG); + private static final Category QUOTA_TRACKER_CATEGORY_DISABLED = new Category("disabled"); /** * Queue of pending jobs. The JobServiceContext class will receive jobs from this list @@ -493,10 +502,18 @@ public class JobSchedulerService extends com.android.server.SystemService } switch (name) { case Constants.KEY_ENABLE_API_QUOTAS: + case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC: case Constants.KEY_API_QUOTA_SCHEDULE_COUNT: case Constants.KEY_API_QUOTA_SCHEDULE_WINDOW_MS: case Constants.KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT: case Constants.KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT: + case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS: if (!apiQuotaScheduleUpdated) { mConstants.updateApiQuotaConstantsLocked(); updateQuotaTracker(); @@ -583,10 +600,26 @@ public class JobSchedulerService extends com.android.server.SystemService @VisibleForTesting void updateQuotaTracker() { - mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS); + mQuotaTracker.setEnabled( + mConstants.ENABLE_API_QUOTAS || mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC); mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, mConstants.API_QUOTA_SCHEDULE_COUNT, mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_REG, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_ANR, + mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, + mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS); } /** @@ -616,6 +649,8 @@ public class JobSchedulerService extends com.android.server.SystemService "conn_low_signal_strength_relax_frac"; private static final String KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = "prefetch_force_batch_relax_threshold_ms"; + // This has been enabled for 3+ full releases. We're unlikely to disable it. + // TODO(141645789): remove this flag private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas"; private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count"; private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms"; @@ -623,6 +658,22 @@ public class JobSchedulerService extends com.android.server.SystemService "aq_schedule_throw_exception"; private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = "aq_schedule_return_failure"; + private static final String KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC = + "enable_execution_safeguards_udc"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = + "es_u_timeout_uij_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = + "es_u_timeout_ej_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = + "es_u_timeout_reg_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = + "es_u_timeout_total_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = + "es_u_timeout_window_ms"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = + "es_u_anr_count"; + private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = + "es_u_anr_window_ms"; private static final String KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = "runtime_free_quota_max_limit_ms"; @@ -662,6 +713,17 @@ public class JobSchedulerService extends com.android.server.SystemService private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true; private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; + private static final boolean DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC = true; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + // EJs have a shorter timeout, so set a higher limit for them to start with. + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 5; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 3; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = 10; + private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = + 24 * HOUR_IN_MILLIS; + private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = 3; + private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = + 6 * HOUR_IN_MILLIS; @VisibleForTesting public static final long DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 30 * MINUTE_IN_MILLIS; @VisibleForTesting @@ -774,6 +836,55 @@ public class JobSchedulerService extends com.android.server.SystemService public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT; + /** + * Whether to enable the execution safeguards added in UDC. + */ + public boolean ENABLE_EXECUTION_SAFEGUARDS_UDC = DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC; + /** + * The maximum number of times an app can have a user-iniated job time out before the system + * begins removing some of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT; + /** + * The maximum number of times an app can have an expedited job time out before the system + * begins removing some of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT; + /** + * The maximum number of times an app can have a regular job time out before the system + * begins removing some of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT; + /** + * The maximum number of times an app can have jobs time out before the system + * attempts to restrict most of the app's privileges. + */ + public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT; + /** + * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT}, + * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT}, + * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT}, and + * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT} should be evaluated over. + */ + public long EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS; + + /** + * The maximum number of times an app can ANR from JobScheduler's perspective before + * JobScheduler will attempt to restrict the app. + */ + public int EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT; + /** + * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_ANR_COUNT} + * should be evaluated over. + */ + public long EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = + DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS; + /** The maximum amount of time we will let a job run for when quota is "free". */ public long RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; @@ -915,6 +1026,9 @@ public class JobSchedulerService extends com.android.server.SystemService private void updateApiQuotaConstantsLocked() { ENABLE_API_QUOTAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_ENABLE_API_QUOTAS, DEFAULT_ENABLE_API_QUOTAS); + ENABLE_EXECUTION_SAFEGUARDS_UDC = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC); // Set a minimum value on the quota limit so it's not so low that it interferes with // legitimate use cases. API_QUOTA_SCHEDULE_COUNT = Math.max(250, @@ -931,6 +1045,40 @@ public class JobSchedulerService extends com.android.server.SystemService DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT); + + // Set a minimum value on the timeout limit so it's not so low that it interferes with + // legitimate use cases. + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = Math.max(2, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = Math.max(2, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = Math.max(2, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT)); + final int highestTimeoutCount = Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = Math.max(highestTimeoutCount, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT)); + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS); + EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = Math.max(1, + DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT)); + EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_JOB_SCHEDULER, + KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS, + DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS); } private void updateRuntimeConstantsLocked() { @@ -1029,6 +1177,23 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT, API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println(); + pw.print(KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, ENABLE_EXECUTION_SAFEGUARDS_UDC) + .println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS, + EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT, + EXECUTION_SAFEGUARDS_UDC_ANR_COUNT).println(); + pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS, + EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS).println(); + pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println(); pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println(); pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) @@ -1598,7 +1763,7 @@ public class JobSchedulerService extends com.android.server.SystemService uId, null, jobStatus.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED, JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(), - jobStatus.getJobId(), + jobStatus.getLoggingJobId(), jobStatus.hasChargingConstraint(), jobStatus.hasBatteryNotLowConstraint(), jobStatus.hasStorageNotLowConstraint(), @@ -1631,7 +1796,8 @@ public class JobSchedulerService extends com.android.server.SystemService jobStatus.getEstimatedNetworkDownloadBytes(), jobStatus.getEstimatedNetworkUploadBytes(), jobStatus.getWorkCount(), - ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid()))); + ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())), + jobStatus.getNamespaceHash()); // If the job is immediately ready to run, then we can just immediately // put it in the pending list and try to schedule it. This is especially @@ -2026,7 +2192,7 @@ public class JobSchedulerService extends com.android.server.SystemService cancelled.getSourceUid(), null, cancelled.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED, internalReasonCode, cancelled.getStandbyBucket(), - cancelled.getJobId(), + cancelled.getLoggingJobId(), cancelled.hasChargingConstraint(), cancelled.hasBatteryNotLowConstraint(), cancelled.hasStorageNotLowConstraint(), @@ -2059,7 +2225,8 @@ public class JobSchedulerService extends com.android.server.SystemService cancelled.getEstimatedNetworkDownloadBytes(), cancelled.getEstimatedNetworkUploadBytes(), cancelled.getWorkCount(), - ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid()))); + ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())), + cancelled.getNamespaceHash()); } // If this is a replacement, bring in the new version of the job if (incomingJob != null) { @@ -2250,12 +2417,52 @@ public class JobSchedulerService extends com.android.server.SystemService // Set up the app standby bucketing tracker mStandbyTracker = new StandbyTracker(); mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); - mQuotaTracker = new CountQuotaTracker(context, QUOTA_CATEGORIZER); - mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED, - mConstants.API_QUOTA_SCHEDULE_COUNT, - mConstants.API_QUOTA_SCHEDULE_WINDOW_MS); + + final Categorizer quotaCategorizer = (userId, packageName, tag) -> { + if (QUOTA_TRACKER_TIMEOUT_UIJ_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_TIMEOUT_EJ_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_TIMEOUT_REG_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_REG + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_TIMEOUT_TOTAL_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_ANR_TAG.equals(tag)) { + return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC + ? QUOTA_TRACKER_CATEGORY_ANR + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) { + return mConstants.ENABLE_API_QUOTAS + ? QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + if (QUOTA_TRACKER_SCHEDULE_LOGGED.equals(tag)) { + return mConstants.ENABLE_API_QUOTAS + ? QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED + : QUOTA_TRACKER_CATEGORY_DISABLED; + } + Slog.wtf(TAG, "Unexpected category tag: " + tag); + return QUOTA_TRACKER_CATEGORY_DISABLED; + }; + mQuotaTracker = new CountQuotaTracker(context, quotaCategorizer); + updateQuotaTracker(); // Log at most once per minute. + // Set outside updateQuotaTracker() since this is intentionally not configurable. mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000); + mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_DISABLED, Integer.MAX_VALUE, 60_000); mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); mAppStandbyInternal.addListener(mStandbyTracker); @@ -2760,6 +2967,48 @@ public class JobSchedulerService extends com.android.server.SystemService 0 /* Reset cumulativeExecutionTime because of successful execution */); } + @VisibleForTesting + void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) { + boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT; + // If madeActive = 0, the job never actually started. + if (!jobTimedOut && jobStatus.madeActive > 0) { + final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive; + // The debug reason may be different if we stopped the job for some other reason + // (eg. constraints), so look at total execution time to be safe. + if (jobStatus.startedAsUserInitiatedJob) { + // TODO: factor in different min guarantees for different UI job types + jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + } else if (jobStatus.startedAsExpeditedJob) { + jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + } else { + jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_GUARANTEE_MS; + } + } + if (jobTimedOut) { + final int userId = jobStatus.getTimeoutBlameUserId(); + final String pkg = jobStatus.getTimeoutBlamePackageName(); + mQuotaTracker.noteEvent(userId, pkg, + jobStatus.startedAsUserInitiatedJob + ? QUOTA_TRACKER_TIMEOUT_UIJ_TAG + : (jobStatus.startedAsExpeditedJob + ? QUOTA_TRACKER_TIMEOUT_EJ_TAG + : QUOTA_TRACKER_TIMEOUT_REG_TAG)); + if (!mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_TIMEOUT_TOTAL_TAG)) { + mAppStandbyInternal.restrictApp( + pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); + } + } + + if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_ANR) { + final int callingUserId = jobStatus.getUserId(); + final String callingPkg = jobStatus.getServiceComponent().getPackageName(); + if (!mQuotaTracker.noteEvent(callingUserId, callingPkg, QUOTA_TRACKER_ANR_TAG)) { + mAppStandbyInternal.restrictApp(callingPkg, callingUserId, + UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY); + } + } + } + // JobCompletedListener implementations. /** @@ -2782,6 +3031,8 @@ public class JobSchedulerService extends com.android.server.SystemService mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; + maybeProcessBuggyJob(jobStatus, debugStopReason); + if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_UNINSTALL || debugStopReason == JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED) { // The job should have already been cleared from the rest of the JS tracking. No need @@ -3509,26 +3760,36 @@ public class JobSchedulerService extends com.android.server.SystemService if (job.shouldTreatAsUserInitiatedJob() && checkRunUserInitiatedJobsPermission( job.getSourceUid(), job.getSourcePackageName())) { + // The calling package is the one doing the work, so use it in the + // timeout quota checks. + final boolean isWithinTimeoutQuota = mQuotaTracker.isWithinQuota( + job.getTimeoutBlameUserId(), job.getTimeoutBlamePackageName(), + QUOTA_TRACKER_TIMEOUT_UIJ_TAG); + final long upperLimitMs = isWithinTimeoutQuota + ? mConstants.RUNTIME_UI_LIMIT_MS + : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; if (job.getJob().getRequiredNetwork() != null) { // User-initiated data transfers. if (mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS) { final long estimatedTransferTimeMs = mConnectivityController.getEstimatedTransferTimeMs(job); if (estimatedTransferTimeMs == ConnectivityController.UNKNOWN_TIME) { - return mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; + return Math.min(upperLimitMs, + mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS); } // Try to give the job at least as much time as we think the transfer // will take, but cap it at the maximum limit. final long factoredTransferTimeMs = (long) (estimatedTransferTimeMs * mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR); - return Math.min(mConstants.RUNTIME_UI_LIMIT_MS, + return Math.min(upperLimitMs, Math.max(factoredTransferTimeMs, mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); } - return Math.max(mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, - mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS); + return Math.min(upperLimitMs, + Math.max(mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); } - return mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + return Math.min(upperLimitMs, mConstants.RUNTIME_MIN_UI_GUARANTEE_MS); } else if (job.shouldTreatAsExpeditedJob()) { // Don't guarantee RESTRICTED jobs more than 5 minutes. return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX @@ -3545,13 +3806,24 @@ public class JobSchedulerService extends com.android.server.SystemService synchronized (mLock) { if (job.shouldTreatAsUserInitiatedJob() && checkRunUserInitiatedJobsPermission( - job.getSourceUid(), job.getSourcePackageName())) { + job.getSourceUid(), job.getSourcePackageName()) + && mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), + job.getTimeoutBlamePackageName(), + QUOTA_TRACKER_TIMEOUT_UIJ_TAG)) { return mConstants.RUNTIME_UI_LIMIT_MS; } if (job.shouldTreatAsUserInitiatedJob()) { return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; } - return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + // Only let the app use the higher runtime if it hasn't repeatedly timed out. + final String timeoutTag = job.shouldTreatAsExpeditedJob() + ? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG; + final long upperLimitMs = + mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(), + job.getTimeoutBlamePackageName(), timeoutTag) + ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS + : mConstants.RUNTIME_MIN_GUARANTEE_MS; + return Math.min(upperLimitMs, mConstants.USE_TARE_POLICY ? mTareController.getMaxJobExecutionTimeMsLocked(job) : mQuotaController.getMaxJobExecutionTimeMsLocked(job)); @@ -3795,6 +4067,17 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override + public boolean isAppConsideredBuggy(int callingUserId, @NonNull String callingPackageName, + int timeoutBlameUserId, @NonNull String timeoutBlamePackageName) { + return !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName, + QUOTA_TRACKER_ANR_TAG) + || !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName, + QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG) + || !mQuotaTracker.isWithinQuota(timeoutBlameUserId, timeoutBlamePackageName, + QUOTA_TRACKER_TIMEOUT_TOTAL_TAG); + } + + @Override public boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId, int userId, @NonNull String packageName) { if (packageName == null) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index bf2e4560a4ef..109686d76b2f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -413,16 +413,22 @@ public final class JobServiceContext implements ServiceConnection { final Intent intent = new Intent().setComponent(job.getServiceComponent()) .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; + boolean startedWithForegroundFlag = false; try { final Context.BindServiceFlags bindFlags; - if (job.shouldTreatAsUserInitiatedJob()) { + if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) { + // If the user has bg restricted the app, don't give the job FG privileges + // such as bypassing data saver or getting the higher foreground proc state. + // If we've gotten to this point, the app is most likely in the foreground, + // so the job will run just fine while the user keeps the app in the foreground. bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); - } else if (job.shouldTreatAsExpeditedJob()) { + startedWithForegroundFlag = true; + } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND @@ -465,7 +471,8 @@ public final class JobServiceContext implements ServiceConnection { job.getSourceUid(), null, job.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED, JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, - job.getStandbyBucket(), job.getJobId(), + job.getStandbyBucket(), + job.getLoggingJobId(), job.hasChargingConstraint(), job.hasBatteryNotLowConstraint(), job.hasStorageNotLowConstraint(), @@ -498,7 +505,8 @@ public final class JobServiceContext implements ServiceConnection { job.getEstimatedNetworkDownloadBytes(), job.getEstimatedNetworkUploadBytes(), job.getWorkCount(), - ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid()))); + ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())), + job.getNamespaceHash()); sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount()); final String sourcePackage = job.getSourcePackageName(); if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { @@ -533,8 +541,11 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; + // Wait until after bindService() returns a success value to set these so we don't + // have JobStatus objects that aren't running but have these set to true. job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); job.startedAsUserInitiatedJob = job.shouldTreatAsUserInitiatedJob(); + job.startedWithForegroundFlag = startedWithForegroundFlag; return true; } } @@ -1329,7 +1340,7 @@ public final class JobServiceContext implements ServiceConnection { // FINISHED/NO-RETRY. onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true, /* texCounterMetricId */ - "job_scheduler.value_cntr_w_uid_slow_app_response_onStartJob", + "job_scheduler.value_cntr_w_uid_slow_app_response_on_start_job", /* debugReason */ "timed out while starting", /* anrMessage */ "No response to onStartJob", CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES, @@ -1341,7 +1352,7 @@ public final class JobServiceContext implements ServiceConnection { // other reason. onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ false, /* texCounterMetricId */ - "job_scheduler.value_cntr_w_uid_slow_app_response_onStopJob", + "job_scheduler.value_cntr_w_uid_slow_app_response_on_stop_job", /* debugReason */ "timed out while stopping", /* anrMessage */ "No response to onStopJob", CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES, @@ -1398,7 +1409,7 @@ public final class JobServiceContext implements ServiceConnection { } else if (mAwaitingNotification) { onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true, /* texCounterMetricId */ - "job_scheduler.value_cntr_w_uid_slow_app_response_setNotification", + "job_scheduler.value_cntr_w_uid_slow_app_response_set_notification", /* debugReason */ "timed out while stopping", /* anrMessage */ "required notification not provided", /* triggerAnr */ true); @@ -1521,7 +1532,8 @@ public final class JobServiceContext implements ServiceConnection { FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, completedJob.getSourceUid(), null, completedJob.getBatteryName(), FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED, - loggingInternalStopReason, completedJob.getStandbyBucket(), completedJob.getJobId(), + loggingInternalStopReason, completedJob.getStandbyBucket(), + completedJob.getLoggingJobId(), completedJob.hasChargingConstraint(), completedJob.hasBatteryNotLowConstraint(), completedJob.hasStorageNotLowConstraint(), @@ -1555,7 +1567,8 @@ public final class JobServiceContext implements ServiceConnection { completedJob.getEstimatedNetworkUploadBytes(), completedJob.getWorkCount(), ActivityManager - .processStateAmToProto(mService.getUidProcState(completedJob.getUid()))); + .processStateAmToProto(mService.getUidProcState(completedJob.getUid())), + completedJob.getNamespaceHash()); if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) { Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler", getId()); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index ecee10a13c1d..25b3421a55f0 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.os.SystemClock; import android.os.UserHandle; @@ -205,8 +206,32 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); - final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, - jobStatus.canRunInBatterySaver()); + final boolean isUserBgRestricted = + !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() + && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); + // If a job started with the foreground flag, it'll cause the UID to stay active + // and thus cause areJobsRestricted() to always return false, so if + // areJobsRestricted() returns false and the app is BG restricted and not TOP, + // we need to stop any jobs that started with the foreground flag so they don't + // keep the app in an elevated proc state. If we were to get in this situation, + // then the user restricted the app after the job started, so it's best to stop + // the job as soon as possible, especially since the job would be visible to the + // user (with a notification and in Task Manager). + // There are several other reasons that uidActive can be true for an app even if its + // proc state is less important than BFGS. + // JobScheduler has historically (at least up through UDC) allowed the app's jobs to run + // when its UID was active, even if it's background restricted. This has been fine because + // JobScheduler stops the job as soon as the UID becomes inactive and the jobs themselves + // will not keep the UID active. The logic here is to ensure that special jobs + // (e.g. user-initiated jobs) themselves do not keep the UID active when the app is + // background restricted. + final boolean shouldStopImmediately = jobStatus.startedWithForegroundFlag + && isUserBgRestricted + && mService.getUidProcState(uid) + > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; + final boolean canRun = !shouldStopImmediately + && !mAppStateTracker.areJobsRestricted( + uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isActive; if (activeState == UNKNOWN) { @@ -219,8 +244,7 @@ public final class BackgroundJobsController extends StateController { } boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun, - !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() - && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)); + isUserBgRestricted); didChange |= jobStatus.setUidActive(isActive); return didChange; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index f6bdb9303a04..6d938debde10 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -1774,6 +1774,12 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); + if (mBackgroundMeteredAllowed.size() > 0) { + pw.print("Background metered allowed: "); + pw.println(mBackgroundMeteredAllowed); + pw.println(); + } + pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index b5d763c17e59..13903acc0439 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -43,6 +43,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; import android.text.format.DateFormat; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Pair; @@ -51,6 +52,7 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -65,10 +67,12 @@ import com.android.server.job.JobStatusShortInfoProto; import dalvik.annotation.optimization.NeverCompile; import java.io.PrintWriter; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Objects; +import java.util.Random; import java.util.function.Predicate; /** @@ -88,6 +92,13 @@ public final class JobStatus { private static final String TAG = "JobScheduler.JobStatus"; static final boolean DEBUG = JobSchedulerService.DEBUG; + private static MessageDigest sMessageDigest; + /** Cache of namespace to hash to reduce how often we need to generate the namespace hash. */ + @GuardedBy("sNamespaceHashCache") + private static final ArrayMap<String, String> sNamespaceHashCache = new ArrayMap<>(); + /** Maximum size of {@link #sNamespaceHashCache}. */ + private static final int MAX_NAMESPACE_CACHE_SIZE = 128; + private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10; public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; @@ -231,6 +242,10 @@ public final class JobStatus { final String sourceTag; @Nullable private final String mNamespace; + @Nullable + private final String mNamespaceHash; + /** An ID that can be used to uniquely identify the job when logging statsd metrics. */ + private final long mLoggingJobId; final String tag; @@ -415,6 +430,13 @@ public final class JobStatus { * when it started running. This isn't copied over when a job is rescheduled. */ public boolean startedAsUserInitiatedJob = false; + /** + * Whether this particular JobStatus instance started with the foreground flag + * (or more accurately, did <b>not</b> have the + * {@link android.content.Context#BIND_NOT_FOREGROUND} flag + * included in its binding flags when started). + */ + public boolean startedWithForegroundFlag = false; public boolean startedWithImmediacyPrivilege = false; @@ -568,6 +590,8 @@ public final class JobStatus { this.callingUid = callingUid; this.standbyBucket = standbyBucket; mNamespace = namespace; + mNamespaceHash = generateNamespaceHash(namespace); + mLoggingJobId = generateLoggingId(namespace, job.getId()); int tempSourceUid = -1; if (sourceUserId != -1 && sourcePackageName != null) { @@ -804,6 +828,63 @@ public final class JobStatus { /*innerFlags=*/ 0, /* dynamicConstraints */ 0); } + private long generateLoggingId(@Nullable String namespace, int jobId) { + if (namespace == null) { + return jobId; + } + return ((long) namespace.hashCode()) << 31 | jobId; + } + + @Nullable + private static String generateNamespaceHash(@Nullable String namespace) { + if (namespace == null) { + return null; + } + if (namespace.trim().isEmpty()) { + // Input is composed of all spaces (or nothing at all). + return namespace; + } + synchronized (sNamespaceHashCache) { + final int idx = sNamespaceHashCache.indexOfKey(namespace); + if (idx >= 0) { + return sNamespaceHashCache.valueAt(idx); + } + } + String hash = null; + try { + // .hashCode() can result in conflicts that would make distinguishing between + // namespaces hard and reduce the accuracy of certain metrics. Use SHA-256 + // to generate the hash since the probability of collision is extremely low. + if (sMessageDigest == null) { + sMessageDigest = MessageDigest.getInstance("SHA-256"); + } + final byte[] digest = sMessageDigest.digest(namespace.getBytes()); + // Convert to hexadecimal representation + StringBuilder hexBuilder = new StringBuilder(digest.length); + for (byte byteChar : digest) { + hexBuilder.append(String.format("%02X", byteChar)); + } + hash = hexBuilder.toString(); + } catch (Exception e) { + Slog.wtf(TAG, "Couldn't hash input", e); + } + if (hash == null) { + // If we get to this point, something went wrong with the MessageDigest above. + // Don't return the raw input value (which would defeat the purpose of hashing). + return "failed_namespace_hash"; + } + hash = hash.intern(); + synchronized (sNamespaceHashCache) { + if (sNamespaceHashCache.size() >= MAX_NAMESPACE_CACHE_SIZE) { + // Drop a random mapping instead of dropping at a predefined index to avoid + // potentially always dropping the same mapping. + sNamespaceHashCache.removeAt((new Random()).nextInt(MAX_NAMESPACE_CACHE_SIZE)); + } + sNamespaceHashCache.put(namespace, hash); + } + return hash; + } + public void enqueueWorkLocked(JobWorkItem work) { if (pendingWork == null) { pendingWork = new ArrayList<>(); @@ -956,6 +1037,11 @@ public final class JobStatus { return job.getId(); } + /** Returns an ID that can be used to uniquely identify the job when logging statsd metrics. */ + public long getLoggingJobId() { + return mLoggingJobId; + } + public void printUniqueId(PrintWriter pw) { if (mNamespace != null) { pw.print(mNamespace); @@ -1009,13 +1095,77 @@ public final class JobStatus { return UserHandle.getUserId(callingUid); } + private boolean shouldBlameSourceForTimeout() { + // If the system scheduled the job on behalf of an app, assume the app is the one + // doing the work and blame the app directly. This is the case with things like + // syncs via SyncManager. + // If the system didn't schedule the job on behalf of an app, then + // blame the app doing the actual work. Proxied jobs are a little tricky. + // Proxied jobs scheduled by built-in system apps like DownloadManager may be fine + // and we could consider exempting those jobs. For example, in DownloadManager's + // case, all it does is download files and the code is vetted. A timeout likely + // means it's downloading a large file, which isn't an error. For now, DownloadManager + // is an exempted app, so this shouldn't be an issue. + // However, proxied jobs coming from other system apps (such as those that can + // be updated separately from an OTA) may not be fine and we would want to apply + // this policy to those jobs/apps. + // TODO(284512488): consider exempting DownloadManager or other system apps + return UserHandle.isCore(callingUid); + } + + /** + * Returns the package name that should most likely be blamed for the job timing out. + */ + public String getTimeoutBlamePackageName() { + if (shouldBlameSourceForTimeout()) { + return sourcePackageName; + } + return getServiceComponent().getPackageName(); + } + + /** + * Returns the UID that should most likely be blamed for the job timing out. + */ + public int getTimeoutBlameUid() { + if (shouldBlameSourceForTimeout()) { + return sourceUid; + } + return callingUid; + } + + /** + * Returns the userId that should most likely be blamed for the job timing out. + */ + public int getTimeoutBlameUserId() { + if (shouldBlameSourceForTimeout()) { + return sourceUserId; + } + return UserHandle.getUserId(callingUid); + } + /** * Returns an appropriate standby bucket for the job, taking into account any standby * exemptions. */ public int getEffectiveStandbyBucket() { + final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class); + final boolean isBuggy = jsi.isAppConsideredBuggy( + getUserId(), getServiceComponent().getPackageName(), + getTimeoutBlameUserId(), getTimeoutBlamePackageName()); + final int actualBucket = getStandbyBucket(); if (actualBucket == EXEMPTED_INDEX) { + // EXEMPTED apps always have their jobs exempted, even if they're buggy, because the + // user has explicitly told the system to avoid restricting the app for power reasons. + if (isBuggy) { + final String pkg; + if (getServiceComponent().getPackageName().equals(sourcePackageName)) { + pkg = sourcePackageName; + } else { + pkg = getServiceComponent().getPackageName() + "/" + sourcePackageName; + } + Slog.w(TAG, "Exempted app " + pkg + " considered buggy"); + } return actualBucket; } if (uidActive || getJob().isExemptedFromAppStandby()) { @@ -1023,13 +1173,18 @@ public final class JobStatus { // like other ACTIVE apps. return ACTIVE_INDEX; } + // If the app is considered buggy, but hasn't yet been put in the RESTRICTED bucket + // (potentially because it's used frequently by the user), limit its effective bucket + // so that it doesn't get to run as much as a normal ACTIVE app. + final int highestBucket = isBuggy ? WORKING_INDEX : ACTIVE_INDEX; if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX && mHasMediaBackupExemption) { - // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the + // Treat it as if it's at least WORKING_INDEX since media backup jobs are important + // to the user, and the // source package may not have been used directly in a while. - return Math.min(WORKING_INDEX, actualBucket); + return Math.max(highestBucket, Math.min(WORKING_INDEX, actualBucket)); } - return actualBucket; + return Math.max(highestBucket, actualBucket); } /** Returns the real standby bucket of the job. */ @@ -1102,10 +1257,16 @@ public final class JobStatus { return true; } + @Nullable public String getNamespace() { return mNamespace; } + @Nullable + public String getNamespaceHash() { + return mNamespaceHash; + } + public String getSourceTag() { return sourceTag; } @@ -1452,6 +1613,10 @@ public final class JobStatus { * for any reason. */ public boolean shouldTreatAsUserInitiatedJob() { + // isUserBgRestricted is intentionally excluded from this method. It should be fine to + // treat the job as a UI job while the app is TOP, but just not in the background. + // Instead of adding a proc state check here, the parts of JS that can make the distinction + // and care about the distinction can do the check. return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; @@ -1499,6 +1664,11 @@ public final class JobStatus { && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); } + /** Returns whether or not the app is background restricted by the user (FAS). */ + public boolean isUserBgRestricted() { + return mIsUserBgRestricted; + } + /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); @@ -2648,6 +2818,12 @@ public final class JobStatus { } pw.decreaseIndent(); + pw.print("Started with foreground flag: "); + pw.println(startedWithForegroundFlag); + if (mIsUserBgRestricted) { + pw.println("User BG restricted"); + } + if (changedAuthorities != null) { pw.println("Changed authorities:"); pw.increaseIndent(); 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 175c8d1cc4f3..1c29982dbd48 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 @@ -773,6 +773,14 @@ public final class QuotaController extends StateController { // If quota is currently "free", then the job can run for the full amount of time, // regardless of bucket (hence using charging instead of isQuotaFreeLocked()). if (mService.isBatteryCharging() + // The top and foreground cases here were added because apps in those states + // aren't really restricted and the work could be something the user is + // waiting for. Now that user-initiated jobs are a defined concept, we may + // not need these exemptions as much. However, UIJs are currently limited + // (as of UDC) to data transfer work. There may be other work that could + // rely on this exception. Once we add more UIJ types, we can re-evaluate + // the need for these exceptions. + // TODO: re-evaluate the need for these exceptions || mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { @@ -3153,7 +3161,8 @@ public final class QuotaController extends StateController { private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20; private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS; - private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 45 * MINUTE_IN_MILLIS; + // TODO(267949143): set a different limit for headless system apps + private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 60 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS; private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS; private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS; diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 55e681521048..7d3837786be9 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -3025,7 +3025,7 @@ public class AppStandbyController public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = COMPRESS_TIME ? ONE_MINUTE : 30 * ONE_MINUTE; public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = - COMPRESS_TIME ? ONE_MINUTE : ONE_DAY; + COMPRESS_TIME ? ONE_MINUTE : ONE_HOUR; public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true; public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS = 2 * ONE_MINUTE; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9eb9d66cb71a..9bd29700a12d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3568,6 +3568,8 @@ package android.view { field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000 field public static final int FLAG_SLIPPERY = 536870912; // 0x20000000 field public CharSequence accessibilityTitle; + field public float preferredMaxDisplayRefreshRate; + field public float preferredMinDisplayRefreshRate; field public int privateFlags; } diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index 12026aa3f72a..4cad58521c09 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -568,13 +568,13 @@ public abstract class Animator implements Cloneable { * repetition. lastPlayTime is similar and is used to calculate how many repeats have been * done between the two times. */ - void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) {} + void animateValuesInRange(long currentPlayTime, long lastPlayTime) {} /** * Internal use only. This animates any animation that has ended since lastPlayTime. * If an animation hasn't been finished, no change will be made. */ - void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) {} + void animateSkipToEnds(long currentPlayTime, long lastPlayTime) {} /** * Internal use only. Adds all start times (after delay) to and end times to times. diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java index f69bbfd3c53f..2198fcd70637 100644 --- a/core/java/android/animation/AnimatorInflater.java +++ b/core/java/android/animation/AnimatorInflater.java @@ -111,19 +111,20 @@ public class AnimatorInflater { float pathErrorScale) throws NotFoundException { final ConfigurationBoundResourceCache<Animator> animatorCache = resources .getAnimatorCache(); - Animator animator = animatorCache.getInstance(id, resources, theme); - if (animator != null) { + ConfigurationBoundResourceCache.Entry<Animator> animatorEntry = + animatorCache.getInstance(id, resources, theme); + if (animatorEntry.hasValue()) { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id)); } - return animator; + return animatorEntry.getValue(); } else if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "cache miss for animator " + resources.getResourceName(id)); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); - animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); + Animator animator = createAnimatorFromXml(resources, theme, parser, pathErrorScale); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<Animator> constantState = animator.createConstantState(); @@ -131,7 +132,7 @@ public class AnimatorInflater { if (DBG_ANIMATOR_INFLATER) { Log.d(TAG, "caching animator for res " + resources.getResourceName(id)); } - animatorCache.put(id, theme, constantState); + animatorCache.put(id, theme, constantState, animatorEntry.getGeneration()); // create a new animator so that cached version is never used by the user animator = constantState.newInstance(resources, theme); } @@ -160,20 +161,22 @@ public class AnimatorInflater { final ConfigurationBoundResourceCache<StateListAnimator> cache = resources .getStateListAnimatorCache(); final Theme theme = context.getTheme(); - StateListAnimator animator = cache.getInstance(id, resources, theme); - if (animator != null) { - return animator; + ConfigurationBoundResourceCache.Entry<StateListAnimator> animatorEntry = + cache.getInstance(id, resources, theme); + if (animatorEntry.hasValue()) { + return animatorEntry.getValue(); } XmlResourceParser parser = null; try { parser = resources.getAnimation(id); - animator = createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); + StateListAnimator animator = + createStateListAnimatorFromXml(context, parser, Xml.asAttributeSet(parser)); if (animator != null) { animator.appendChangingConfigurations(getChangingConfigs(resources, id)); final ConstantState<StateListAnimator> constantState = animator .createConstantState(); if (constantState != null) { - cache.put(id, theme, constantState); + cache.put(id, theme, constantState, animatorEntry.getGeneration()); // return a clone so that the animator in constant state is never used. animator = constantState.newInstance(resources, theme); } diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 60659dc12342..70c3d7ae3f82 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -825,8 +825,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim private void animateBasedOnPlayTime( long currentPlayTime, long lastPlayTime, - boolean inReverse, - boolean notify + boolean inReverse ) { if (currentPlayTime < 0 || lastPlayTime < -1) { throw new UnsupportedOperationException("Error: Play time should never be negative."); @@ -857,8 +856,8 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim while (index < endIndex) { long playTime = startEndTimes[index]; if (lastPlayTime != playTime) { - animateSkipToEnds(playTime, lastPlayTime, notify); - animateValuesInRange(playTime, lastPlayTime, notify); + animateSkipToEnds(playTime, lastPlayTime); + animateValuesInRange(playTime, lastPlayTime); lastPlayTime = playTime; } index++; @@ -868,15 +867,15 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim index--; long playTime = startEndTimes[index]; if (lastPlayTime != playTime) { - animateSkipToEnds(playTime, lastPlayTime, notify); - animateValuesInRange(playTime, lastPlayTime, notify); + animateSkipToEnds(playTime, lastPlayTime); + animateValuesInRange(playTime, lastPlayTime); lastPlayTime = playTime; } } } if (currentPlayTime != lastPlayTime) { - animateSkipToEnds(currentPlayTime, lastPlayTime, notify); - animateValuesInRange(currentPlayTime, lastPlayTime, notify); + animateSkipToEnds(currentPlayTime, lastPlayTime); + animateValuesInRange(currentPlayTime, lastPlayTime); } } @@ -896,13 +895,11 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } @Override - void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) { + void animateSkipToEnds(long currentPlayTime, long lastPlayTime) { initAnimation(); if (lastPlayTime > currentPlayTime) { - if (notify) { - notifyStartListeners(true); - } + notifyStartListeners(true); for (int i = mEvents.size() - 1; i >= 0; i--) { AnimationEvent event = mEvents.get(i); Node node = event.mNode; @@ -916,31 +913,25 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim if (currentPlayTime <= start && start < lastPlayTime) { animator.animateSkipToEnds( 0, - lastPlayTime - node.mStartTime, - notify + lastPlayTime - node.mStartTime ); - if (notify) { - mPlayingSet.remove(node); - } + mPlayingSet.remove(node); } else if (start <= currentPlayTime && currentPlayTime <= end) { animator.animateSkipToEnds( currentPlayTime - node.mStartTime, - lastPlayTime - node.mStartTime, - notify + lastPlayTime - node.mStartTime ); - if (notify && !mPlayingSet.contains(node)) { + if (!mPlayingSet.contains(node)) { mPlayingSet.add(node); } } } } - if (currentPlayTime <= 0 && notify) { + if (currentPlayTime <= 0) { notifyEndListeners(true); } } else { - if (notify) { - notifyStartListeners(false); - } + notifyStartListeners(false); int eventsSize = mEvents.size(); for (int i = 0; i < eventsSize; i++) { AnimationEvent event = mEvents.get(i); @@ -955,45 +946,39 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim if (lastPlayTime < end && end <= currentPlayTime) { animator.animateSkipToEnds( end - node.mStartTime, - lastPlayTime - node.mStartTime, - notify + lastPlayTime - node.mStartTime ); - if (notify) { - mPlayingSet.remove(node); - } + mPlayingSet.remove(node); } else if (start <= currentPlayTime && currentPlayTime <= end) { animator.animateSkipToEnds( currentPlayTime - node.mStartTime, - lastPlayTime - node.mStartTime, - notify + lastPlayTime - node.mStartTime ); - if (notify && !mPlayingSet.contains(node)) { + if (!mPlayingSet.contains(node)) { mPlayingSet.add(node); } } } } - if (currentPlayTime >= getTotalDuration() && notify) { + if (currentPlayTime >= getTotalDuration()) { notifyEndListeners(false); } } } @Override - void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) { + void animateValuesInRange(long currentPlayTime, long lastPlayTime) { initAnimation(); - if (notify) { - if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) { - notifyStartListeners(false); - } else { - long duration = getTotalDuration(); - if (duration >= 0 - && (lastPlayTime > duration || (lastPlayTime == duration - && currentPlayTime < duration)) - ) { - notifyStartListeners(true); - } + if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) { + notifyStartListeners(false); + } else { + long duration = getTotalDuration(); + if (duration >= 0 + && (lastPlayTime > duration || (lastPlayTime == duration + && currentPlayTime < duration)) + ) { + notifyStartListeners(true); } } @@ -1014,8 +999,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim ) { animator.animateValuesInRange( currentPlayTime - node.mStartTime, - Math.max(-1, lastPlayTime - node.mStartTime), - notify + Math.max(-1, lastPlayTime - node.mStartTime) ); } } @@ -1111,7 +1095,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } } mSeekState.setPlayTime(playTime, mReversing); - animateBasedOnPlayTime(playTime, lastPlayTime, mReversing, true); + animateBasedOnPlayTime(playTime, lastPlayTime, mReversing); } /** @@ -1144,16 +1128,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim private void initChildren() { if (!isInitialized()) { mChildrenInitialized = true; - - // We have to initialize all the start values so that they are based on the previous - // values. - long[] times = ensureChildStartAndEndTimes(); - - long previousTime = -1; - for (long time : times) { - animateBasedOnPlayTime(time, previousTime, false, false); - previousTime = time; - } + skipToEndValue(false); } } @@ -1489,6 +1464,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim anim.mPauseTime = -1; anim.mSeekState = new SeekState(); anim.mSelfPulse = true; + anim.mStartListenersCalled = false; anim.mPlayingSet = new ArrayList<Node>(); anim.mNodeMap = new ArrayMap<Animator, Node>(); anim.mNodes = new ArrayList<Node>(nodeCount); diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index ead238f75ba4..5de7f387b206 100644 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -1417,21 +1417,19 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio * will be called. */ @Override - void animateValuesInRange(long currentPlayTime, long lastPlayTime, boolean notify) { + void animateValuesInRange(long currentPlayTime, long lastPlayTime) { if (currentPlayTime < 0 || lastPlayTime < -1) { throw new UnsupportedOperationException("Error: Play time should never be negative."); } initAnimation(); long duration = getTotalDuration(); - if (notify) { - if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) { - notifyStartListeners(false); - } else if (lastPlayTime > duration - || (lastPlayTime == duration && currentPlayTime < duration) - ) { - notifyStartListeners(true); - } + if (lastPlayTime < 0 || (lastPlayTime == 0 && currentPlayTime > 0)) { + notifyStartListeners(false); + } else if (lastPlayTime > duration + || (lastPlayTime == duration && currentPlayTime < duration) + ) { + notifyStartListeners(true); } if (duration >= 0) { lastPlayTime = Math.min(duration, lastPlayTime); @@ -1448,7 +1446,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio iteration = Math.min(iteration, mRepeatCount); lastIteration = Math.min(lastIteration, mRepeatCount); - if (notify && iteration != lastIteration) { + if (iteration != lastIteration) { notifyListeners(AnimatorCaller.ON_REPEAT, false); } } @@ -1464,7 +1462,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio } @Override - void animateSkipToEnds(long currentPlayTime, long lastPlayTime, boolean notify) { + void animateSkipToEnds(long currentPlayTime, long lastPlayTime) { boolean inReverse = currentPlayTime < lastPlayTime; boolean doSkip; if (currentPlayTime <= 0 && lastPlayTime > 0) { @@ -1474,13 +1472,9 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio doSkip = duration >= 0 && currentPlayTime >= duration && lastPlayTime < duration; } if (doSkip) { - if (notify) { - notifyStartListeners(inReverse); - } + notifyStartListeners(inReverse); skipToEndValue(inReverse); - if (notify) { - notifyEndListeners(inReverse); - } + notifyEndListeners(inReverse); } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 0ec3847d29f4..46260ea5e658 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -607,6 +607,7 @@ interface IActivityManager { void killPackageDependents(in String packageName, int userId); void makePackageIdle(String packageName, int userId); + void setDeterministicUidIdle(boolean deterministic); int getMemoryTrimLevel(); boolean isVrModePackageEnabled(in ComponentName packageName); void notifyLockedProfile(int userId); diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 4b8cfd5d3215..c4e49954f745 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -334,6 +334,12 @@ public class TaskInfo { public boolean isVisible; /** + * Whether this task is request visible. + * @hide + */ + public boolean isVisibleRequested; + + /** * Whether this task is sleeping due to sleeping display. * @hide */ @@ -518,6 +524,7 @@ public class TaskInfo { && Objects.equals(taskDescription, that.taskDescription) && isFocused == that.isFocused && isVisible == that.isVisible + && isVisibleRequested == that.isVisibleRequested && isSleeping == that.isSleeping && Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId) && parentTaskId == that.parentTaskId @@ -591,6 +598,7 @@ public class TaskInfo { parentTaskId = source.readInt(); isFocused = source.readBoolean(); isVisible = source.readBoolean(); + isVisibleRequested = source.readBoolean(); isSleeping = source.readBoolean(); topActivityInSizeCompat = source.readBoolean(); topActivityEligibleForLetterboxEducation = source.readBoolean(); @@ -644,6 +652,7 @@ public class TaskInfo { dest.writeInt(parentTaskId); dest.writeBoolean(isFocused); dest.writeBoolean(isVisible); + dest.writeBoolean(isVisibleRequested); dest.writeBoolean(isSleeping); dest.writeBoolean(topActivityInSizeCompat); dest.writeBoolean(topActivityEligibleForLetterboxEducation); @@ -687,6 +696,7 @@ public class TaskInfo { + " parentTaskId=" + parentTaskId + " isFocused=" + isFocused + " isVisible=" + isVisible + + " isVisibleRequested=" + isVisibleRequested + " isSleeping=" + isSleeping + " topActivityInSizeCompat=" + topActivityInSizeCompat + " topActivityEligibleForLetterboxEducation= " diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 57935e3bd5b1..70e924a2acfe 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -2933,62 +2933,21 @@ public class WallpaperManager { } } - if (!isComponentExist(context, cn)) { - cn = null; - } - - return cn; - } - - /** - * Return {@link ComponentName} of the CMF default wallpaper, or - * {@link #getDefaultWallpaperComponent(Context)} if none is defined. - * - * @hide - */ - public static ComponentName getCmfDefaultWallpaperComponent(Context context) { - ComponentName cn = null; - String[] cmfWallpaperMap = context.getResources().getStringArray( - com.android.internal.R.array.cmf_default_wallpaper_component); - if (cmfWallpaperMap == null || cmfWallpaperMap.length == 0) { - Log.d(TAG, "No CMF wallpaper config"); - return getDefaultWallpaperComponent(context); - } - - for (String entry : cmfWallpaperMap) { - String[] cmfWallpaper; - if (!TextUtils.isEmpty(entry)) { - cmfWallpaper = entry.split(","); - if (cmfWallpaper != null && cmfWallpaper.length == 2 && VALUE_CMF_COLOR.equals( - cmfWallpaper[0]) && !TextUtils.isEmpty(cmfWallpaper[1])) { - cn = ComponentName.unflattenFromString(cmfWallpaper[1]); - break; - } + // Check if the package exists + if (cn != null) { + try { + final PackageManager packageManager = context.getPackageManager(); + packageManager.getPackageInfo(cn.getPackageName(), + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + } catch (PackageManager.NameNotFoundException e) { + cn = null; } } - if (!isComponentExist(context, cn)) { - cn = null; - } - return cn; } - private static boolean isComponentExist(Context context, ComponentName cn) { - if (cn == null) { - return false; - } - try { - final PackageManager packageManager = context.getPackageManager(); - packageManager.getPackageInfo(cn.getPackageName(), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - return true; - } - /** * Register a callback for lock wallpaper observation. Only the OS may use this. * diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 783e7d3e2afc..da5e40aedbd2 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -581,6 +581,7 @@ public class DevicePolicyManager { * <li>{@link #LOCK_TASK_FEATURE_HOME}</li> * <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li> * <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li> + * <li>{@link #LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK}</li> * </ul> * <li>{@link #getLockTaskFeatures(ComponentName)}</li> * <li>{@link #setLockTaskPackages(ComponentName, String[])}</li> @@ -11368,7 +11369,8 @@ public class DevicePolicyManager { * @throws SecurityException if the caller is not a profile owner on an organization-owned * managed profile. * @throws IllegalStateException if called after the device setup has been completed. - * @throws UnsupportedOperationException if the api is not enabled. + * @throws UnsupportedOperationException if managed subscriptions policy is not explicitly + * enabled by the device policy management role holder during device setup. * @see ManagedSubscriptionsPolicy */ public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) { diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 7be00a045403..3487e0b1f3e8 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -827,6 +827,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS = 1 << 4; + /** + * Whether AbiOverride was used when installing this application. + * @hide + */ + public static final int PRIVATE_FLAG_EXT_CPU_OVERRIDE = 1 << 5; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = { PRIVATE_FLAG_EXT_PROFILEABLE, @@ -834,6 +840,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE, PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK, PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS, + PRIVATE_FLAG_EXT_CPU_OVERRIDE, }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlagsExt {} diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java index 77b1954c0f1b..2669040403ea 100644 --- a/core/java/android/content/pm/UserProperties.java +++ b/core/java/android/content/pm/UserProperties.java @@ -184,7 +184,7 @@ public final class UserProperties implements Parcelable { * device policies from its parent profile. * *<p> All the user restrictions and device policies would be not propagated to the profile - * with this property value. The {(TODO:b/256978256) @link DevicePolicyEngine} + * with this property value. The {@link com.android.server.devicepolicy.DevicePolicyEngine} * uses this property to determine and propagate only select ones to the given profile. * * @hide diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java index 5e10a5768358..4da3c18883bd 100644 --- a/core/java/android/content/res/ConfigurationBoundResourceCache.java +++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java @@ -37,16 +37,16 @@ public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<Cons * @param key a key that uniquely identifies the drawable resource * @param resources a Resources object from which to create new instances. * @param theme the theme where the resource will be used - * @return a new instance of the resource, or {@code null} if not in + * @return an Entry wrapping a new instance of the resource, or {@code null} if not in * the cache */ - public T getInstance(long key, Resources resources, Resources.Theme theme) { - final ConstantState<T> entry = get(key, theme); - if (entry != null) { - return entry.newInstance(resources, theme); + public Entry<T> getInstance(long key, Resources resources, Resources.Theme theme) { + final Entry<ConstantState<T>> e = get(key, theme); + if (e.hasValue()) { + return new Entry<>(e.getValue().newInstance(resources, theme), e.getGeneration()); } - return null; + return new Entry<>(null, e.getGeneration()); } @Override diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java index d0ebe3304065..b51d14a3c472 100644 --- a/core/java/android/content/res/DrawableCache.java +++ b/core/java/android/content/res/DrawableCache.java @@ -40,14 +40,32 @@ class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> { */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Drawable getInstance(long key, Resources resources, Resources.Theme theme) { - final Drawable.ConstantState entry = get(key, theme); - if (entry != null) { - return entry.newDrawable(resources, theme); + final Entry<Drawable.ConstantState> entry = get(key, theme); + if (entry.getValue() != null) { + return entry.getValue().newDrawable(resources, theme); } return null; } + /** + * If the resource is cached, creates and returns a new instance of it. + * + * @param key a key that uniquely identifies the drawable resource + * @param resources a Resources object from which to create new instances. + * @param theme the theme where the resource will be used + * @return an Entry wrapping a a new instance of the resource, or {@code null} if not in + * the cache + */ + public Entry<Drawable> getDrawable(long key, Resources resources, Resources.Theme theme) { + final Entry<Drawable.ConstantState> e = get(key, theme); + if (e.hasValue()) { + return new Entry<>(e.getValue().newDrawable(resources, theme), e.getGeneration()); + } + + return new Entry<>(null, e.getGeneration()); + } + @Override public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) { return Configuration.needNewResources(configChanges, entry.getChangingConfigurations()); diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 3a2863e5636d..25154d5c1623 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -650,15 +650,21 @@ public class ResourcesImpl { key = (((long) value.assetCookie) << 32) | value.data; } + int cacheGeneration; // First, check whether we have a cached version of this drawable // that was inflated against the specified theme. Skip the cache if // we're currently preloading or we're not using the cache. if (!mPreloading && useCache) { - final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme); - if (cachedDrawable != null) { - cachedDrawable.setChangingConfigurations(value.changingConfigurations); - return cachedDrawable; + final ThemedResourceCache.Entry<Drawable> cachedDrawable = + caches.getDrawable(key, wrapper, theme); + if (cachedDrawable.hasValue()) { + cachedDrawable.getValue().setChangingConfigurations( + value.changingConfigurations); + return cachedDrawable.getValue(); } + cacheGeneration = cachedDrawable.getGeneration(); + } else { + cacheGeneration = ThemedResourceCache.UNDEFINED_GENERATION; } // Next, check preloaded drawables. Preloaded drawables may contain @@ -702,7 +708,8 @@ public class ResourcesImpl { if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); if (useCache) { - cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr); + cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr, + cacheGeneration); if (needsNewDrawableAfterCache) { Drawable.ConstantState state = dr.getConstantState(); if (state != null) { @@ -733,7 +740,7 @@ public class ResourcesImpl { } private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, - Resources.Theme theme, boolean usesTheme, long key, Drawable dr) { + Resources.Theme theme, boolean usesTheme, long key, Drawable dr, int cacheGeneration) { final Drawable.ConstantState cs = dr.getConstantState(); if (cs == null) { return; @@ -761,7 +768,7 @@ public class ResourcesImpl { } } else { synchronized (mAccessLock) { - caches.put(key, theme, cs, usesTheme); + caches.put(key, theme, cs, cacheGeneration, usesTheme); } } } @@ -1002,14 +1009,16 @@ public class ResourcesImpl { TypedValue value, int id) { final long key = (((long) value.assetCookie) << 32) | value.data; final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache; - ComplexColor complexColor = cache.getInstance(key, wrapper, theme); - if (complexColor != null) { - return complexColor; + ThemedResourceCache.Entry<ComplexColor> complexColorEntry = + cache.getInstance(key, wrapper, theme); + if (complexColorEntry.hasValue()) { + return complexColorEntry.getValue(); } final android.content.res.ConstantState<ComplexColor> factory = sPreloadedComplexColors.get(key); + ComplexColor complexColor = null; if (factory != null) { complexColor = factory.newInstance(wrapper, theme); } @@ -1026,7 +1035,8 @@ public class ResourcesImpl { sPreloadedComplexColors.put(key, complexColor.getConstantState()); } } else { - cache.put(key, theme, complexColor.getConstantState()); + cache.put(key, theme, complexColor.getConstantState(), + complexColorEntry.getGeneration()); } } return complexColor; diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java index 3270944ce7e3..e7fd2755a941 100644 --- a/core/java/android/content/res/ThemedResourceCache.java +++ b/core/java/android/content/res/ThemedResourceCache.java @@ -33,11 +33,37 @@ import java.lang.ref.WeakReference; * @param <T> type of data to cache */ abstract class ThemedResourceCache<T> { + public static final int UNDEFINED_GENERATION = -1; @UnsupportedAppUsage private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries; private LongSparseArray<WeakReference<T>> mUnthemedEntries; private LongSparseArray<WeakReference<T>> mNullThemedEntries; + private int mGeneration; + + public static class Entry<S> { + private S mValue; + private int mGeneration; + + + public S getValue() { + return mValue; + } + + public boolean hasValue() { + return mValue != null; + } + + public int getGeneration() { + return mGeneration; + } + + Entry(S value, int generation) { + this.mValue = value; + this.mGeneration = generation; + } + } + /** * Adds a new theme-dependent entry to the cache. * @@ -45,9 +71,10 @@ abstract class ThemedResourceCache<T> { * @param theme the theme against which this entry was inflated, or * {@code null} if the entry has no theme applied * @param entry the entry to cache + * @param generation The generation of the cache to compare against before storing */ - public void put(long key, @Nullable Theme theme, @NonNull T entry) { - put(key, theme, entry, true); + public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation) { + put(key, theme, entry, generation, true); } /** @@ -57,10 +84,12 @@ abstract class ThemedResourceCache<T> { * @param theme the theme against which this entry was inflated, or * {@code null} if the entry has no theme applied * @param entry the entry to cache + * @param generation The generation of the cache to compare against before storing * @param usesTheme {@code true} if the entry is affected theme changes, * {@code false} otherwise */ - public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) { + public void put(long key, @Nullable Theme theme, @NonNull T entry, int generation, + boolean usesTheme) { if (entry == null) { return; } @@ -72,7 +101,8 @@ abstract class ThemedResourceCache<T> { } else { entries = getThemedLocked(theme, true); } - if (entries != null) { + if (entries != null + && ((generation == mGeneration) || (generation == UNDEFINED_GENERATION))) { entries.put(key, new WeakReference<>(entry)); } } @@ -86,7 +116,7 @@ abstract class ThemedResourceCache<T> { * @return a cached entry, or {@code null} if not in the cache */ @Nullable - public T get(long key, @Nullable Theme theme) { + public Entry get(long key, @Nullable Theme theme) { // The themed (includes null-themed) and unthemed caches are mutually // exclusive, so we'll give priority to whichever one we think we'll // hit first. Since most of the framework drawables are themed, that's @@ -96,7 +126,7 @@ abstract class ThemedResourceCache<T> { if (themedEntries != null) { final WeakReference<T> themedEntry = themedEntries.get(key); if (themedEntry != null) { - return themedEntry.get(); + return new Entry(themedEntry.get(), mGeneration); } } @@ -104,12 +134,12 @@ abstract class ThemedResourceCache<T> { if (unthemedEntries != null) { final WeakReference<T> unthemedEntry = unthemedEntries.get(key); if (unthemedEntry != null) { - return unthemedEntry.get(); + return new Entry(unthemedEntry.get(), mGeneration); } } } - return null; + return new Entry(null, mGeneration); } /** @@ -121,6 +151,7 @@ abstract class ThemedResourceCache<T> { @UnsupportedAppUsage public void onConfigurationChange(@Config int configChanges) { prune(configChanges); + mGeneration++; } /** diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index b4533042b4a3..76efce56dcf0 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -53,8 +53,6 @@ import android.view.Surface; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; -import libcore.util.EmptyArray; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; @@ -667,7 +665,7 @@ public final class DisplayManager { } else if (category == null || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) { return getDisplays(displayIds, Objects::nonNull); } - return (Display[]) EmptyArray.OBJECT; + return new Display[0]; } private Display[] getDisplays(int[] displayIds, Predicate<Display> predicate) { diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java index 2e64a74a5d67..ff47f3fc30aa 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -444,7 +444,7 @@ public class VpnManager { * Retrieve the VpnProfileState for the profile provisioned by the calling package. * * @return the VpnProfileState with current information, or null if there was no profile - * provisioned by the calling package. + * provisioned and started by the calling package. */ @Nullable public VpnProfileState getProvisionedVpnProfileState() { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index d1063f647c4f..d1c10fa46aaa 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -190,8 +190,7 @@ public final class PowerManager { /** * Wake lock flag: Turn the screen on when the wake lock is acquired. * <p> - * This flag requires {@link android.Manifest.permission#TURN_SCREEN_ON} for apps targeting - * Android version {@link Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and higher. + * This flag will require {@link android.Manifest.permission#TURN_SCREEN_ON} in future releases. * </p><p> * Normally wake locks don't actually wake the device, they just cause the screen to remain on * once it's already on. This flag will cause the device to wake up when the wake lock is diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 7d68b44581de..84f1880213b8 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -736,7 +736,7 @@ public class UserManager { * The default value is <code>false</code>. * * <p>Holders of the permission - * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS} + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS} * can set this restriction using the DevicePolicyManager APIs mentioned below. * * <p>Key for user restrictions. @@ -941,7 +941,7 @@ public class UserManager { * this restriction will be set as a base restriction which cannot be removed by any admin. * * <p>Holders of the permission - * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS} + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS} * can set this restriction using the DevicePolicyManager APIs mentioned below. * * <p>Key for user restrictions. @@ -1451,7 +1451,7 @@ public class UserManager { * affected. The default value is <code>false</code>. * * <p>Holders of the permission - * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS} + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS} * can set this restriction using the DevicePolicyManager APIs mentioned below. * * <p>Key for user restrictions. @@ -1597,7 +1597,7 @@ public class UserManager { * set. * * <p>Holders of the permission - * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_USERS} + * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MODIFY_USERS} * can set this restriction using the DevicePolicyManager APIs mentioned below. * * <p>The default value is <code>false</code>. diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index 931adb55a686..ef274a56e1d3 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.Activity; +import android.app.ActivityOptions; import android.app.Application.ActivityLifecycleCallbacks; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -535,7 +536,11 @@ public final class PrintManager { return null; } try { - mContext.startIntentSender(intent, null, 0, 0, 0); + ActivityOptions activityOptions = ActivityOptions.makeBasic() + .setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + mContext.startIntentSender(intent, null, 0, 0, 0, + activityOptions.toBundle()); return new PrintJob(printJob, this); } catch (SendIntentException sie) { Log.e(LOG_TAG, "Couldn't start print job config activity.", sie); diff --git a/core/java/android/service/notification/TEST_MAPPING b/core/java/android/service/notification/TEST_MAPPING new file mode 100644 index 000000000000..59b2bc1e4f73 --- /dev/null +++ b/core/java/android/service/notification/TEST_MAPPING @@ -0,0 +1,49 @@ +{ + "presubmit": [ + { + "name": "CtsNotificationTestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + }, + { + "name": "FrameworksUiServicesTests", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsNotificationTestCases" + } + ] +} diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index d87198a0dc85..ff7d8bb956ca 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -238,7 +238,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_ENROLLMENT, "false"); DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_HEARING_AID_PAGE, "true"); DEFAULT_FLAGS.put(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM, "false"); - DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "true"); + DEFAULT_FLAGS.put(SETTINGS_AUDIO_ROUTING, "false"); DEFAULT_FLAGS.put(SETTINGS_FLASH_NOTIFICATIONS, "true"); DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true"); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index f570c6d15672..5019b85ca503 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1099,21 +1099,25 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // TODO: Support a ResultReceiver for IME. // TODO(b/123718661): Make show() work for multi-session IME. int typesReady = 0; + final boolean imeVisible = mState.isSourceOrDefaultVisible( + mImeSourceConsumer.getId(), ime()); for (int type = FIRST; type <= LAST; type = type << 1) { if ((types & type) == 0) { continue; } @AnimationType final int animationType = getAnimationType(type); final boolean requestedVisible = (type & mRequestedVisibleTypes) != 0; - final boolean isImeAnimation = type == ime(); - if (requestedVisible && animationType == ANIMATION_TYPE_NONE - || animationType == ANIMATION_TYPE_SHOW) { + final boolean isIme = type == ime(); + var alreadyVisible = requestedVisible && (!isIme || imeVisible) + && animationType == ANIMATION_TYPE_NONE; + var alreadyAnimatingShow = animationType == ANIMATION_TYPE_SHOW; + if (alreadyVisible || alreadyAnimatingShow) { // no-op: already shown or animating in (because window visibility is // applied before starting animation). if (DEBUG) Log.d(TAG, String.format( "show ignored for type: %d animType: %d requestedVisible: %s", type, animationType, requestedVisible)); - if (isImeAnimation) { + if (isIme) { ImeTracker.forLogging().onCancelled(statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); } @@ -1121,13 +1125,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } if (fromIme && animationType == ANIMATION_TYPE_USER) { // App is already controlling the IME, don't cancel it. - if (isImeAnimation) { + if (isIme) { ImeTracker.forLogging().onFailed( statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); } continue; } - if (isImeAnimation) { + if (isIme) { ImeTracker.forLogging().onProgress( statsToken, ImeTracker.PHASE_CLIENT_APPLY_ANIMATION); } @@ -1374,7 +1378,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation // The requested visibilities should be delayed as well. Otherwise, we might override // the insets visibility before playing animation. - setRequestedVisibleTypes(mReportedRequestedVisibleTypes, typesReady); + setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0); if (!fromIme) { diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java index 470c2801d838..37b6c77e3c74 100644 --- a/core/java/android/view/InsetsFrameProvider.java +++ b/core/java/android/view/InsetsFrameProvider.java @@ -223,6 +223,10 @@ public class InsetsFrameProvider implements Parcelable { if (mArbitraryRectangle != null) { sb.append(", mArbitraryRectangle=").append(mArbitraryRectangle.toShortString()); } + if (mMinimalInsetsSizeInDisplayCutoutSafe != null) { + sb.append(", mMinimalInsetsSizeInDisplayCutoutSafe=") + .append(mMinimalInsetsSizeInDisplayCutoutSafe); + } sb.append("}"); return sb.toString(); } @@ -248,6 +252,7 @@ public class InsetsFrameProvider implements Parcelable { mInsetsSize = in.readTypedObject(Insets.CREATOR); mInsetsSizeOverrides = in.createTypedArray(InsetsSizeOverride.CREATOR); mArbitraryRectangle = in.readTypedObject(Rect.CREATOR); + mMinimalInsetsSizeInDisplayCutoutSafe = in.readTypedObject(Insets.CREATOR); } @Override @@ -258,6 +263,7 @@ public class InsetsFrameProvider implements Parcelable { out.writeTypedObject(mInsetsSize, flags); out.writeTypedArray(mInsetsSizeOverrides, flags); out.writeTypedObject(mArbitraryRectangle, flags); + out.writeTypedObject(mMinimalInsetsSizeInDisplayCutoutSafe, flags); } public boolean idEquals(InsetsFrameProvider o) { @@ -276,13 +282,16 @@ public class InsetsFrameProvider implements Parcelable { return mId == other.mId && mSource == other.mSource && mFlags == other.mFlags && Objects.equals(mInsetsSize, other.mInsetsSize) && Arrays.equals(mInsetsSizeOverrides, other.mInsetsSizeOverrides) - && Objects.equals(mArbitraryRectangle, other.mArbitraryRectangle); + && Objects.equals(mArbitraryRectangle, other.mArbitraryRectangle) + && Objects.equals(mMinimalInsetsSizeInDisplayCutoutSafe, + other.mMinimalInsetsSizeInDisplayCutoutSafe); } @Override public int hashCode() { return Objects.hash(mId, mSource, mFlags, mInsetsSize, - Arrays.hashCode(mInsetsSizeOverrides), mArbitraryRectangle); + Arrays.hashCode(mInsetsSizeOverrides), mArbitraryRectangle, + mMinimalInsetsSizeInDisplayCutoutSafe); } public static final @NonNull Parcelable.Creator<InsetsFrameProvider> CREATOR = diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4cbb0409dafc..a28be4be24ad 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2426,7 +2426,7 @@ public final class ViewRootImpl implements ViewParent, * * @hide */ - void notifyRendererOfExpensiveFrame() { + public void notifyRendererOfExpensiveFrame() { if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.notifyExpensiveFrame(); } @@ -5459,7 +5459,7 @@ public final class ViewRootImpl implements ViewParent, } private void updateRenderHdrSdrRatio() { - mRenderHdrSdrRatio = mDisplay.getHdrSdrRatio(); + mRenderHdrSdrRatio = Math.min(mDesiredHdrSdrRatio, mDisplay.getHdrSdrRatio()); mUpdateHdrSdrRatioInfo = true; } @@ -5487,22 +5487,14 @@ public final class ViewRootImpl implements ViewParent, mHdrSdrRatioChangedListener = null; } else { mHdrSdrRatioChangedListener = display -> { - setTargetHdrSdrRatio(display.getHdrSdrRatio()); + updateRenderHdrSdrRatio(); + invalidate(); }; mDisplay.registerHdrSdrRatioChangedListener(mExecutor, mHdrSdrRatioChangedListener); } } } - /** happylint */ - public void setTargetHdrSdrRatio(float ratio) { - if (mRenderHdrSdrRatio != ratio) { - mRenderHdrSdrRatio = ratio; - mUpdateHdrSdrRatioInfo = true; - invalidate(); - } - } - @Override public void requestChildFocus(View child, View focused) { if (DEBUG_INPUT_RESIZE) { @@ -7017,6 +7009,10 @@ public final class ViewRootImpl implements ViewParent, private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; boolean handled = mHandwritingInitiator.onTouchEvent(event); + if (handled) { + // If handwriting is started, toolkit doesn't receive ACTION_UP. + mLastClickToolType = event.getToolType(event.getActionIndex()); + } mAttachInfo.mUnbufferedDispatchRequested = false; mAttachInfo.mHandlingPointerEvent = true; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e40f8e78dafc..a91781580ac4 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -435,11 +435,15 @@ public interface WindowManager extends ViewManager { int TRANSIT_KEYGUARD_GOING_AWAY = 7; /** * A window is appearing above a locked keyguard. + * @deprecated use {@link #TRANSIT_TO_FRONT} + {@link #TRANSIT_FLAG_KEYGUARD_OCCLUDING} for + * keyguard occluding with Shell transition. * @hide */ int TRANSIT_KEYGUARD_OCCLUDE = 8; /** * A window is made invisible revealing a locked keyguard. + * @deprecated use {@link #TRANSIT_TO_BACK} + {@link #TRANSIT_FLAG_KEYGUARD_UNOCCLUDING} for + * keyguard occluding with Shell transition. * @hide */ int TRANSIT_KEYGUARD_UNOCCLUDE = 9; @@ -562,6 +566,25 @@ public interface WindowManager extends ViewManager { int TRANSIT_FLAG_INVISIBLE = (1 << 10); // 0x400 /** + * Transition flag: Indicates that keyguard will be showing (locked) with this transition, + * which is the opposite of {@link #TRANSIT_FLAG_KEYGUARD_GOING_AWAY}. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_APPEARING = (1 << 11); // 0x800 + + /** + * Transition flag: Indicates that keyguard is becoming hidden by an app + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_OCCLUDING = (1 << 12); // 0x1000 + + /** + * Transition flag: Indicates that keyguard is being revealed after an app was occluding it. + * @hide + */ + int TRANSIT_FLAG_KEYGUARD_UNOCCLUDING = (1 << 13); // 0x2000 + + /** * @hide */ @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = { @@ -576,11 +599,28 @@ public interface WindowManager extends ViewManager { TRANSIT_FLAG_KEYGUARD_GOING_AWAY, TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT, TRANSIT_FLAG_INVISIBLE, + TRANSIT_FLAG_KEYGUARD_APPEARING, + TRANSIT_FLAG_KEYGUARD_OCCLUDING, + TRANSIT_FLAG_KEYGUARD_UNOCCLUDING }) @Retention(RetentionPolicy.SOURCE) @interface TransitionFlags {} /** + * Transit flags used to signal keyguard visibility is changing for animations. + * + * <p>These roughly correspond to CLOSE, OPEN, TO_BACK, and TO_FRONT on a hypothetical Keyguard + * container. Since Keyguard isn't a container we can't include it in changes and need to send + * this information in its own channel. + * @hide + */ + int KEYGUARD_VISIBILITY_TRANSIT_FLAGS = + (TRANSIT_FLAG_KEYGUARD_GOING_AWAY + | TRANSIT_FLAG_KEYGUARD_APPEARING + | TRANSIT_FLAG_KEYGUARD_OCCLUDING + | TRANSIT_FLAG_KEYGUARD_UNOCCLUDING); + + /** * Remove content mode: Indicates remove content mode is currently not defined. * @hide */ @@ -3763,6 +3803,7 @@ public interface WindowManager extends ViewManager { * This value is ignored if {@link #preferredDisplayModeId} is set. * @hide */ + @TestApi public float preferredMinDisplayRefreshRate; /** @@ -3771,6 +3812,7 @@ public interface WindowManager extends ViewManager { * This value is ignored if {@link #preferredDisplayModeId} is set. * @hide */ + @TestApi public float preferredMaxDisplayRefreshRate; /** Indicates whether this window wants the HDR conversion is disabled. */ diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index 5ad74c8803f4..461b78ce4fc2 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -283,6 +283,9 @@ public class AutofillFeatureFlags { private static final String DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS = "3,4"; private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES = true; private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_MULTILINE_FILTER = true; + private static final boolean + DEFAULT_AFAA_SHOULD_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE = true; + // END AUTOFILL FOR ALL APPS DEFAULTS private AutofillFeatureFlags() {}; @@ -414,7 +417,8 @@ public class AutofillFeatureFlags { public static boolean shouldIncludeAllViewsAutofillTypeNotNoneInAssistStructrue() { return DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_AUTOFILL, - DEVICE_CONFIG_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE, false); + DEVICE_CONFIG_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE, + DEFAULT_AFAA_SHOULD_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE); } /** diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ea750765799f..739c1bfccd3b 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2065,7 +2065,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get enabled autofill services status."); + throw new RuntimeException("Fail to get enabled autofill services status. " + e); } } @@ -2084,7 +2084,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get autofill services component name."); + throw new RuntimeException("Fail to get autofill services component name. " + e); } } @@ -2111,7 +2111,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get user data id for field classification."); + throw new RuntimeException("Fail to get user data id for field classification. " + e); } } @@ -2134,7 +2134,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get user data for field classification."); + throw new RuntimeException("Fail to get user data for field classification. " + e); } } @@ -2174,7 +2174,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get field classification enabled status."); + throw new RuntimeException("Fail to get field classification enabled status. " + e); } } @@ -2198,7 +2198,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get default field classification algorithm."); + throw new RuntimeException("Fail to get default field classification algorithm. " + e); } } @@ -2220,7 +2220,8 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get available field classification algorithms."); + throw new + RuntimeException("Fail to get available field classification algorithms. " + e); } } @@ -2244,7 +2245,7 @@ public final class AutofillManager { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (SyncResultReceiver.TimeoutException e) { - throw new RuntimeException("Fail to get autofill supported status."); + throw new RuntimeException("Fail to get autofill supported status. " + e); } } diff --git a/core/java/android/view/contentcapture/OWNERS b/core/java/android/view/contentcapture/OWNERS index d1eda9648520..a958bbec8f90 100644 --- a/core/java/android/view/contentcapture/OWNERS +++ b/core/java/android/view/contentcapture/OWNERS @@ -6,4 +6,5 @@ markpun@google.com lpeter@google.com tymtsai@google.com hackz@google.com -volnov@google.com
\ No newline at end of file +shivanker@google.com +volnov@google.com diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 8b04d35734ec..63bf5622fbb8 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.app.RemoteAction; import android.content.Context; @@ -301,7 +302,8 @@ public final class TextClassification implements Parcelable { Objects.requireNonNull(intent); return v -> { try { - intent.send(); + intent.send(ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle()); } catch (PendingIntent.CanceledException e) { Log.e(LOG_TAG, "Error sending PendingIntent", e); } diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java index 1d5c6f1f12b1..6f229f6e0054 100644 --- a/core/java/android/webkit/SslErrorHandler.java +++ b/core/java/android/webkit/SslErrorHandler.java @@ -20,11 +20,15 @@ import android.annotation.SystemApi; import android.os.Handler; /** - * Represents a request for handling an SSL error. Instances of this class are - * created by the WebView and passed to - * {@link WebViewClient#onReceivedSslError}. The host application must call - * either {@link #proceed} or {@link #cancel} to set the WebView's response - * to the request. + * Represents a request for handling an SSL error. + * + * <p>A {@link WebView} creates an instance of this class. The instance is + * passed to {@link WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError)}. + * + * <p>The host application must call {@link #cancel()} or, contrary to secure + * web communication standards, {@link #proceed()} to provide the web view's + * response to the request. */ public class SslErrorHandler extends Handler { @@ -35,17 +39,29 @@ public class SslErrorHandler extends Handler { public SslErrorHandler() {} /** - * Proceed with the SSL certificate. - * <p> - * It is not recommended to proceed past SSL errors and this method should - * generally not be used; see {@link WebViewClient#onReceivedSslError} for - * more information. + * Instructs the {@code WebView} that encountered the SSL certificate error + * to ignore the error and continue communicating with the server. + * + * <p class="warning"><b>Warning:</b> When an SSL error occurs, the host + * application should always call {@link #cancel()} rather than + * {@code proceed()} because an invalid SSL certificate means the connection + * is not secure. + * + * @see WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError) */ public void proceed() {} /** - * Cancel this request and all pending requests for the WebView that had - * the error. + * Instructs the {@code WebView} that encountered the SSL certificate error + * to terminate communication with the server. Cancels the current server + * request and all pending requests for the {@code WebView}. + * + * <p>The host application must call this method to prevent a resource from + * loading when an SSL certificate is invalid. + * + * @see WebViewClient#onReceivedSslError(WebView, SslErrorHandler, + * SslError) */ public void cancel() {} } diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 55f09f110f88..2dfeae3567e5 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -382,30 +382,34 @@ public class WebViewClient { } /** - * Notify the host application that an SSL error occurred while loading a - * resource. The host application must call either {@link SslErrorHandler#cancel} or - * {@link SslErrorHandler#proceed}. Note that the decision may be retained for use in - * response to future SSL errors. The default behavior is to cancel the - * load. - * <p> - * This API is only called for recoverable SSL certificate errors. In the case of - * non-recoverable errors (such as when the server fails the client), WebView will call {@link - * #onReceivedError(WebView, WebResourceRequest, WebResourceError)} with {@link - * #ERROR_FAILED_SSL_HANDSHAKE}. - * <p> - * Applications are advised not to prompt the user about SSL errors, as - * the user is unlikely to be able to make an informed security decision - * and WebView does not provide any UI for showing the details of the + * Notifies the host application that an SSL error occurred while loading a + * resource. The host application must call either + * {@link SslErrorHandler#cancel()} or {@link SslErrorHandler#proceed()}. + * + * <p class="warning"><b>Warning:</b> Application overrides of this method + * can be used to display custom error pages or to silently log issues, but + * the host application should always call {@code SslErrorHandler#cancel()} + * and never proceed past errors. + * + * <p class="note"><b>Note:</b> Do not prompt the user about SSL errors. + * Users are unlikely to be able to make an informed security decision, and + * {@code WebView} does not provide a UI for showing the details of the * error in a meaningful way. - * <p> - * Application overrides of this method may display custom error pages or - * silently log issues, but it is strongly recommended to always call - * {@link SslErrorHandler#cancel} and never allow proceeding past errors. * - * @param view The WebView that is initiating the callback. - * @param handler An {@link SslErrorHandler} that will handle the user's - * response. - * @param error The SSL error object. + * <p>The decision to call {@code proceed()} or {@code cancel()} may be + * retained to facilitate responses to future SSL errors. The default + * behavior is to cancel the resource loading process. + * + * <p>This API is called only for recoverable SSL certificate errors. For + * non-recoverable errors (such as when the server fails the client), the + * {@code WebView} calls {@link #onReceivedError(WebView, + * WebResourceRequest, WebResourceError) onReceivedError(WebView, + * WebResourceRequest, WebResourceError)} with the + * {@link #ERROR_FAILED_SSL_HANDSHAKE} argument. + * + * @param view {@code WebView} that initiated the callback. + * @param handler {@link SslErrorHandler} that handles the user's response. + * @param error SSL error object. */ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 8c05130bf5fe..d2a16a3a9212 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -99,9 +99,6 @@ public final class TransitionInfo implements Parcelable { /** The container is the display. */ public static final int FLAG_IS_DISPLAY = 1 << 5; - /** The container can show on top of lock screen. */ - public static final int FLAG_OCCLUDES_KEYGUARD = 1 << 6; - /** * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is * used to prevent seamless rotation. @@ -175,7 +172,6 @@ public final class TransitionInfo implements Parcelable { FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT, FLAG_IS_VOICE_INTERACTION, FLAG_IS_DISPLAY, - FLAG_OCCLUDES_KEYGUARD, FLAG_DISPLAY_HAS_ALERT_WINDOWS, FLAG_IS_INPUT_METHOD, FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, @@ -424,8 +420,8 @@ public final class TransitionInfo implements Parcelable { case TRANSIT_NONE: return "NONE"; case TRANSIT_OPEN: return "OPEN"; case TRANSIT_CLOSE: return "CLOSE"; - case TRANSIT_TO_FRONT: return "SHOW"; - case TRANSIT_TO_BACK: return "HIDE"; + case TRANSIT_TO_FRONT: return "TO_FRONT"; + case TRANSIT_TO_BACK: return "TO_BACK"; case TRANSIT_CHANGE: return "CHANGE"; default: return "<unknown:" + mode + ">"; } @@ -457,9 +453,6 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_IS_DISPLAY) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("IS_DISPLAY"); } - if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) { - sb.append(sb.length() == 0 ? "" : "|").append("OCCLUDES_KEYGUARD"); - } if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("DISPLAY_HAS_ALERT_WINDOWS"); } @@ -557,6 +550,16 @@ public final class TransitionInfo implements Parcelable { } /** + * Updates the callsites of all the surfaces in this transition, which aids in the debugging of + * lingering surfaces. + */ + public void setUnreleasedWarningCallSiteForAllSurfaces(String callsite) { + for (int i = mChanges.size() - 1; i >= 0; --i) { + mChanges.get(i).getLeash().setUnreleasedWarningCallSite(callsite); + } + } + + /** * Makes a copy of this as if it were parcel'd and unparcel'd. This implies that surfacecontrol * refcounts are incremented which allows the "remote" receiver to release them without breaking * the caller's references. Use this only if you need to "send" this to a local function which diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index 421d9983d6b7..632208cdb97c 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -163,19 +163,25 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { // Re-populate the top callback to WM if the removed callback was previously the top one. if (previousTopCallback == callback) { // We should call onBackCancelled() when an active callback is removed from dispatcher. - if (mProgressAnimator.isBackAnimationInProgress() - && callback instanceof OnBackAnimationCallback) { - // The ProgressAnimator will handle the new topCallback, so we don't want to call - // onBackCancelled() on it. We call immediately the callback instead. - OnBackAnimationCallback animatedCallback = (OnBackAnimationCallback) callback; - animatedCallback.onBackCancelled(); - Log.d(TAG, "The callback was removed while a back animation was in progress, " - + "an onBackCancelled() was dispatched."); - } + sendCancelledIfInProgress(callback); setTopOnBackInvokedCallback(getTopCallback()); } } + private void sendCancelledIfInProgress(@NonNull OnBackInvokedCallback callback) { + boolean isInProgress = mProgressAnimator.isBackAnimationInProgress(); + if (isInProgress && callback instanceof OnBackAnimationCallback) { + OnBackAnimationCallback animatedCallback = (OnBackAnimationCallback) callback; + animatedCallback.onBackCancelled(); + if (DEBUG) { + Log.d(TAG, "sendCancelIfRunning: callback canceled"); + } + } else { + Log.w(TAG, "sendCancelIfRunning: isInProgress=" + isInProgress + + "callback=" + callback); + } + } + @Override public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { registerOnBackInvokedCallbackUnchecked(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM); @@ -188,9 +194,20 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mImeDispatcher = null; } if (!mAllCallbacks.isEmpty()) { + OnBackInvokedCallback topCallback = getTopCallback(); + if (topCallback != null) { + sendCancelledIfInProgress(topCallback); + } else { + // Should not be possible + Log.e(TAG, "There is no topCallback, even if mAllCallbacks is not empty"); + } // Clear binder references in WM. setTopOnBackInvokedCallback(null); } + + // We should also stop running animations since all callbacks have been removed. + // note: mSpring.skipToEnd(), in ProgressAnimator.reset(), requires the main handler. + Handler.getMain().post(mProgressAnimator::reset); mAllCallbacks.clear(); mOnBackInvokedCallbacks.clear(); } @@ -342,12 +359,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void onBackInvoked() throws RemoteException { Handler.getMain().post(() -> { + boolean isInProgress = mProgressAnimator.isBackAnimationInProgress(); mProgressAnimator.reset(); final OnBackInvokedCallback callback = mCallbackRef.get(); if (callback == null) { Log.d(TAG, "Trying to call onBackInvoked() on a null callback reference."); return; } + if (callback instanceof OnBackAnimationCallback && !isInProgress) { + Log.w(TAG, "ProgressAnimator was not in progress, skip onBackInvoked()."); + return; + } callback.onBackInvoked(); }); } diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index bf265689c0e0..4e7bfe50cd30 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -16,42 +16,50 @@ package com.android.internal.app; -import static android.graphics.PixelFormat.TRANSLUCENT; +import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; import android.animation.ObjectAnimator; +import android.animation.TimeAnimator; +import android.annotation.SuppressLint; import android.app.ActionBar; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.CombinedVibration; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; +import android.os.VibrationEffect; +import android.os.VibratorManager; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.OvershootInterpolator; -import android.widget.AnalogClock; +import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.R; import org.json.JSONObject; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; +import java.util.Random; /** * @hide @@ -59,30 +67,160 @@ import java.time.ZonedDateTime; public class PlatLogoActivity extends Activity { private static final String TAG = "PlatLogoActivity"; - private static final String S_EGG_UNLOCK_SETTING = "egg_mode_s"; + private static final long LAUNCH_TIME = 5000L; + + private static final String U_EGG_UNLOCK_SETTING = "egg_mode_u"; + + private static final float MIN_WARP = 1f; + private static final float MAX_WARP = 10f; // after all these years + private static final boolean FINISH_AFTER_NEXT_STAGE_LAUNCH = false; - private SettableAnalogClock mClock; private ImageView mLogo; - private BubblesDrawable mBg; + private Starfield mStarfield; + + private FrameLayout mLayout; + + private TimeAnimator mAnim; + private ObjectAnimator mWarpAnim; + private Random mRandom; + private float mDp; + + private RumblePack mRumble; + + private boolean mAnimationsEnabled = true; + + private final View.OnTouchListener mTouchListener = new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + measureTouchPressure(event); + startWarp(); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + stopWarp(); + break; + } + return true; + } + + }; + + private final Runnable mLaunchNextStage = () -> { + stopWarp(); + launchNextStage(false); + }; + + private final TimeAnimator.TimeListener mTimeListener = new TimeAnimator.TimeListener() { + @Override + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + mStarfield.update(deltaTime); + final float warpFrac = (mStarfield.getWarp() - MIN_WARP) / (MAX_WARP - MIN_WARP); + if (mAnimationsEnabled) { + mLogo.setTranslationX(mRandom.nextFloat() * warpFrac * 5 * mDp); + mLogo.setTranslationY(mRandom.nextFloat() * warpFrac * 5 * mDp); + } + if (warpFrac > 0f) { + mRumble.rumble(warpFrac); + } + mLayout.postInvalidate(); + } + }; + + private class RumblePack implements Handler.Callback { + private static final int MSG = 6464; + private static final int INTERVAL = 50; + + private final VibratorManager mVibeMan; + private final HandlerThread mVibeThread; + private final Handler mVibeHandler; + private boolean mSpinPrimitiveSupported; + + private long mLastVibe = 0; + + @SuppressLint("MissingPermission") + @Override + public boolean handleMessage(Message msg) { + final float warpFrac = msg.arg1 / 100f; + if (mSpinPrimitiveSupported) { + if (msg.getWhen() > mLastVibe + INTERVAL) { + mLastVibe = msg.getWhen(); + mVibeMan.vibrate(CombinedVibration.createParallel( + VibrationEffect.startComposition() + .addPrimitive(PRIMITIVE_SPIN, (float) Math.pow(warpFrac, 3.0)) + .compose() + )); + } + } else { + if (mRandom.nextFloat() < warpFrac) { + mLogo.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); + } + } + return false; + } + RumblePack() { + mVibeMan = getSystemService(VibratorManager.class); + mSpinPrimitiveSupported = mVibeMan.getDefaultVibrator() + .areAllPrimitivesSupported(PRIMITIVE_SPIN); + + mVibeThread = new HandlerThread("VibratorThread"); + mVibeThread.start(); + mVibeHandler = Handler.createAsync(mVibeThread.getLooper(), this); + } + + public void destroy() { + mVibeThread.quit(); + } + + private void rumble(float warpFrac) { + if (!mVibeThread.isAlive()) return; + + final Message msg = Message.obtain(); + msg.what = MSG; + msg.arg1 = (int) (warpFrac * 100); + mVibeHandler.removeMessages(MSG); + mVibeHandler.sendMessage(msg); + } + + } @Override - protected void onPause() { - super.onPause(); + protected void onDestroy() { + mRumble.destroy(); + + super.onDestroy(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setDecorFitsSystemWindows(false); getWindow().setNavigationBarColor(0); getWindow().setStatusBarColor(0); + getWindow().getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); final ActionBar ab = getActionBar(); if (ab != null) ab.hide(); - final FrameLayout layout = new FrameLayout(this); + try { + mAnimationsEnabled = Settings.Global.getFloat(getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE) > 0f; + } catch (Settings.SettingNotFoundException e) { + mAnimationsEnabled = true; + } - mClock = new SettableAnalogClock(this); + mRumble = new RumblePack(); + + mLayout = new FrameLayout(this); + mRandom = new Random(); + mDp = getResources().getDisplayMetrics().density; + mStarfield = new Starfield(mRandom, mDp * 2f); + mStarfield.setVelocity( + 200f * (mRandom.nextFloat() - 0.5f), + 200f * (mRandom.nextFloat() - 0.5f)); + mLayout.setBackground(mStarfield); final DisplayMetrics dm = getResources().getDisplayMetrics(); final float dp = dm.density; @@ -90,22 +228,79 @@ public class PlatLogoActivity extends Activity { final int widgetSize = (int) (minSide * 0.75); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(widgetSize, widgetSize); lp.gravity = Gravity.CENTER; - layout.addView(mClock, lp); mLogo = new ImageView(this); - mLogo.setVisibility(View.GONE); mLogo.setImageResource(R.drawable.platlogo); - layout.addView(mLogo, lp); + mLogo.setOnTouchListener(mTouchListener); + mLogo.requestFocus(); + mLayout.addView(mLogo, lp); + + Log.v(TAG, "Hello"); + + setContentView(mLayout); + } + + private void startAnimating() { + mAnim = new TimeAnimator(); + mAnim.setTimeListener(mTimeListener); + mAnim.start(); + } + + private void stopAnimating() { + mAnim.cancel(); + mAnim = null; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SPACE) { + if (event.getRepeatCount() == 0) { + startWarp(); + } + return true; + } + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_SPACE) { + stopWarp(); + return true; + } + return false; + } - mBg = new BubblesDrawable(); - mBg.setLevel(0); - mBg.avoid = widgetSize / 2; - mBg.padding = 0.5f * dp; - mBg.minR = 1 * dp; - layout.setBackground(mBg); - layout.setOnLongClickListener(mBg); + private void startWarp() { + stopWarp(); + mWarpAnim = ObjectAnimator.ofFloat(mStarfield, "warp", MIN_WARP, MAX_WARP) + .setDuration(LAUNCH_TIME); + mWarpAnim.start(); + + mLogo.postDelayed(mLaunchNextStage, LAUNCH_TIME + 1000L); + } - setContentView(layout); + private void stopWarp() { + if (mWarpAnim != null) { + mWarpAnim.cancel(); + mWarpAnim.removeAllListeners(); + mWarpAnim = null; + } + mStarfield.setWarp(1f); + mLogo.removeCallbacks(mLaunchNextStage); + } + + @Override + public void onResume() { + super.onResume(); + startAnimating(); + } + + @Override + public void onPause() { + stopWarp(); + stopAnimating(); + super.onPause(); } private boolean shouldWriteSettings() { @@ -113,38 +308,14 @@ public class PlatLogoActivity extends Activity { } private void launchNextStage(boolean locked) { - mClock.animate() - .alpha(0f).scaleX(0.5f).scaleY(0.5f) - .withEndAction(() -> mClock.setVisibility(View.GONE)) - .start(); - - mLogo.setAlpha(0f); - mLogo.setScaleX(0.5f); - mLogo.setScaleY(0.5f); - mLogo.setVisibility(View.VISIBLE); - mLogo.animate() - .alpha(1f) - .scaleX(1f) - .scaleY(1f) - .setInterpolator(new OvershootInterpolator()) - .start(); - - mLogo.postDelayed(() -> { - final ObjectAnimator anim = ObjectAnimator.ofInt(mBg, "level", 0, 10000); - anim.setInterpolator(new DecelerateInterpolator(1f)); - anim.start(); - }, - 500 - ); - final ContentResolver cr = getContentResolver(); try { if (shouldWriteSettings()) { - Log.v(TAG, "Saving egg unlock=" + locked); + Log.v(TAG, "Saving egg locked=" + locked); syncTouchPressure(); Settings.System.putLong(cr, - S_EGG_UNLOCK_SETTING, + U_EGG_UNLOCK_SETTING, locked ? 0 : System.currentTimeMillis()); } } catch (RuntimeException e) { @@ -152,14 +323,18 @@ public class PlatLogoActivity extends Activity { } try { - startActivity(new Intent(Intent.ACTION_MAIN) + final Intent eggActivity = new Intent(Intent.ACTION_MAIN) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) - .addCategory("com.android.internal.category.PLATLOGO")); + .addCategory("com.android.internal.category.PLATLOGO"); + Log.v(TAG, "launching: " + eggActivity); + startActivity(eggActivity); } catch (ActivityNotFoundException ex) { Log.e("com.android.internal.app.PlatLogoActivity", "No more eggs."); } - //finish(); // no longer finish upon unlock; it's fun to frob the dial + if (FINISH_AFTER_NEXT_STAGE_LAUNCH) { + finish(); // we're done here. + } } static final String TOUCH_STATS = "touch.stats"; @@ -217,266 +392,111 @@ public class PlatLogoActivity extends Activity { super.onStop(); } - /** - * Subclass of AnalogClock that allows the user to flip up the glass and adjust the hands. - */ - public class SettableAnalogClock extends AnalogClock { - private int mOverrideHour = -1; - private int mOverrideMinute = -1; - private boolean mOverride = false; + private static class Starfield extends Drawable { + private static final int NUM_STARS = 34; // Build.VERSION_CODES.UPSIDE_DOWN_CAKE + + private static final int NUM_PLANES = 2; + private final float[] mStars = new float[NUM_STARS * 4]; + private float mVx, mVy; + private long mDt = 0; + private final Paint mStarPaint; - public SettableAnalogClock(Context context) { - super(context); + private final Random mRng; + private final float mSize; + + private final Rect mSpace = new Rect(); + private float mWarp = 1f; + + private float mBuffer; + + public void setWarp(float warp) { + mWarp = warp; } - @Override - protected Instant now() { - final Instant realNow = super.now(); - final ZoneId tz = Clock.systemDefaultZone().getZone(); - final ZonedDateTime zdTime = realNow.atZone(tz); - if (mOverride) { - if (mOverrideHour < 0) { - mOverrideHour = zdTime.getHour(); - } - return Clock.fixed(zdTime - .withHour(mOverrideHour) - .withMinute(mOverrideMinute) - .withSecond(0) - .toInstant(), tz).instant(); - } else { - return realNow; - } + public float getWarp() { + return mWarp; } - double toPositiveDegrees(double rad) { - return (Math.toDegrees(rad) + 360 - 90) % 360; + Starfield(Random rng, float size) { + mRng = rng; + mSize = size; + mStarPaint = new Paint(); + mStarPaint.setStyle(Paint.Style.STROKE); + mStarPaint.setColor(Color.WHITE); } @Override - public boolean onTouchEvent(MotionEvent ev) { - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mOverride = true; - // pass through - case MotionEvent.ACTION_MOVE: - measureTouchPressure(ev); - - float x = ev.getX(); - float y = ev.getY(); - float cx = getWidth() / 2f; - float cy = getHeight() / 2f; - float angle = (float) toPositiveDegrees(Math.atan2(x - cx, y - cy)); - - int minutes = (75 - (int) (angle / 6)) % 60; - int minuteDelta = minutes - mOverrideMinute; - if (minuteDelta != 0) { - if (Math.abs(minuteDelta) > 45 && mOverrideHour >= 0) { - int hourDelta = (minuteDelta < 0) ? 1 : -1; - mOverrideHour = (mOverrideHour + 24 + hourDelta) % 24; - } - mOverrideMinute = minutes; - if (mOverrideMinute == 0) { - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - if (getScaleX() == 1f) { - setScaleX(1.05f); - setScaleY(1.05f); - animate().scaleX(1f).scaleY(1f).setDuration(150).start(); - } - } else { - performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); - } - - onTimeChanged(); - postInvalidate(); - } - - return true; - case MotionEvent.ACTION_UP: - if (mOverrideMinute == 0 && (mOverrideHour % 12) == 1) { - Log.v(TAG, "13:00"); - performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - launchNextStage(false); - } - return true; + public void onBoundsChange(Rect bounds) { + mSpace.set(bounds); + mBuffer = mSize * NUM_PLANES * 2 * MAX_WARP; + mSpace.inset(-(int) mBuffer, -(int) mBuffer); + final float w = mSpace.width(); + final float h = mSpace.height(); + for (int i = 0; i < NUM_STARS; i++) { + mStars[4 * i] = mRng.nextFloat() * w; + mStars[4 * i + 1] = mRng.nextFloat() * h; + mStars[4 * i + 2] = mStars[4 * i]; + mStars[4 * i + 3] = mStars[4 * i + 1]; } - return false; } - } - private static final String[][] EMOJI_SETS = { - {"🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🥭", "🍎", "🍏", "🍐", "🍑", - "🍒", "🍓", "🫐", "🥝"}, - {"😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾"}, - {"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "🙃", "🫠", "😉", "😊", - "😇", "🥰", "😍", "🤩", "😘", "😗", "☺️", "😚", "😙", "🥲", "😋", "😛", "😜", - "🤪", "😝", "🤑", "🤗", "🤭", "🫢", "🫣", "🤫", "🤔", "🫡", "🤐", "🤨", "😐", - "😑", "😶", "🫥", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪", "🤤", "😴", - "😷"}, - { "🤩", "😍", "🥰", "😘", "🥳", "🥲", "🥹" }, - { "🫠" }, - {"💘", "💝", "💖", "💗", "💓", "💞", "💕", "❣", "💔", "❤", "🧡", "💛", - "💚", "💙", "💜", "🤎", "🖤", "🤍"}, - // {"👁", "️🫦", "👁️"}, // this one is too much - {"👽", "🛸", "✨", "🌟", "💫", "🚀", "🪐", "🌙", "⭐", "🌍"}, - {"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘"}, - {"🐙", "🪸", "🦑", "🦀", "🦐", "🐡", "🦞", "🐠", "🐟", "🐳", "🐋", "🐬", "🫧", "🌊", - "🦈"}, - {"🙈", "🙉", "🙊", "🐵", "🐒"}, - {"♈", "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓"}, - {"🕛", "🕧", "🕐", "🕜", "🕑", "🕝", "🕒", "🕞", "🕓", "🕟", "🕔", "🕠", "🕕", "🕡", - "🕖", "🕢", "🕗", "🕣", "🕘", "🕤", "🕙", "🕥", "🕚", "🕦"}, - {"🌺", "🌸", "💮", "🏵️", "🌼", "🌿"}, - {"🐢", "✨", "🌟", "👑"} - }; - - static class Bubble { - public float x, y, r; - public int color; - public String text = null; - } - - class BubblesDrawable extends Drawable implements View.OnLongClickListener { - private static final int MAX_BUBBS = 2000; - - private final int[] mColorIds = { - android.R.color.system_accent3_400, - android.R.color.system_accent3_500, - android.R.color.system_accent3_600, - - android.R.color.system_accent2_400, - android.R.color.system_accent2_500, - android.R.color.system_accent2_600, - }; - - private int[] mColors = new int[mColorIds.length]; - - private int mEmojiSet = -1; - - private final Bubble[] mBubbs = new Bubble[MAX_BUBBS]; - private int mNumBubbs; - - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - public float avoid = 0f; - public float padding = 0f; - public float minR = 0f; - - BubblesDrawable() { - for (int i = 0; i < mColorIds.length; i++) { - mColors[i] = getColor(mColorIds[i]); - } - for (int j = 0; j < mBubbs.length; j++) { - mBubbs[j] = new Bubble(); - } + public void setVelocity(float x, float y) { + mVx = x; + mVy = y; } @Override - public void draw(Canvas canvas) { - if (getLevel() == 0) return; - final float f = getLevel() / 10000f; - mPaint.setStyle(Paint.Style.FILL); - mPaint.setTextAlign(Paint.Align.CENTER); - int drawn = 0; - for (int j = 0; j < mNumBubbs; j++) { - if (mBubbs[j].color == 0 || mBubbs[j].r == 0) continue; - if (mBubbs[j].text != null) { - mPaint.setTextSize(mBubbs[j].r * 1.75f); - canvas.drawText(mBubbs[j].text, mBubbs[j].x, - mBubbs[j].y + mBubbs[j].r * f * 0.6f, mPaint); - } else { - mPaint.setColor(mBubbs[j].color); - canvas.drawCircle(mBubbs[j].x, mBubbs[j].y, mBubbs[j].r * f, mPaint); + public void draw(@NonNull Canvas canvas) { + final float dtSec = mDt / 1000f; + final float dx = (mVx * dtSec * mWarp); + final float dy = (mVy * dtSec * mWarp); + + final boolean inWarp = mWarp > 1f; + + canvas.drawColor(Color.BLACK); // 0xFF16161D); + + if (mDt > 0 && mDt < 1000) { + canvas.translate( + -(mBuffer) + mRng.nextFloat() * (mWarp - 1f), + -(mBuffer) + mRng.nextFloat() * (mWarp - 1f) + ); + final float w = mSpace.width(); + final float h = mSpace.height(); + for (int i = 0; i < NUM_STARS; i++) { + final int plane = (int) ((((float) i) / NUM_STARS) * NUM_PLANES) + 1; + mStars[4 * i + 2] = (mStars[4 * i + 2] + dx * plane + w) % w; + mStars[4 * i + 3] = (mStars[4 * i + 3] + dy * plane + h) % h; + mStars[4 * i + 0] = inWarp ? mStars[4 * i + 2] - dx * mWarp * 2 * plane : -100; + mStars[4 * i + 1] = inWarp ? mStars[4 * i + 3] - dy * mWarp * 2 * plane : -100; } - drawn++; } - } - - public void chooseEmojiSet() { - mEmojiSet = (int) (Math.random() * EMOJI_SETS.length); - final String[] emojiSet = EMOJI_SETS[mEmojiSet]; - for (int j = 0; j < mBubbs.length; j++) { - mBubbs[j].text = emojiSet[(int) (Math.random() * emojiSet.length)]; + final int slice = (mStars.length / NUM_PLANES / 4) * 4; + for (int p = 0; p < NUM_PLANES; p++) { + mStarPaint.setStrokeWidth(mSize * (p + 1)); + if (inWarp) { + canvas.drawLines(mStars, p * slice, slice, mStarPaint); + } + canvas.drawPoints(mStars, p * slice, slice, mStarPaint); } - invalidateSelf(); } @Override - protected boolean onLevelChange(int level) { - invalidateSelf(); - return true; - } + public void setAlpha(int alpha) { - @Override - protected void onBoundsChange(Rect bounds) { - super.onBoundsChange(bounds); - randomize(); - } - - private void randomize() { - final float w = getBounds().width(); - final float h = getBounds().height(); - final float maxR = Math.min(w, h) / 3f; - mNumBubbs = 0; - if (avoid > 0f) { - mBubbs[mNumBubbs].x = w / 2f; - mBubbs[mNumBubbs].y = h / 2f; - mBubbs[mNumBubbs].r = avoid; - mBubbs[mNumBubbs].color = 0; - mNumBubbs++; - } - for (int j = 0; j < MAX_BUBBS; j++) { - // a simple but time-tested bubble-packing algorithm: - // 1. pick a spot - // 2. shrink the bubble until it is no longer overlapping any other bubble - // 3. if the bubble hasn't popped, keep it - int tries = 5; - while (tries-- > 0) { - float x = (float) Math.random() * w; - float y = (float) Math.random() * h; - float r = Math.min(Math.min(x, w - x), Math.min(y, h - y)); - - // shrink radius to fit other bubbs - for (int i = 0; i < mNumBubbs; i++) { - r = (float) Math.min(r, - Math.hypot(x - mBubbs[i].x, y - mBubbs[i].y) - mBubbs[i].r - - padding); - if (r < minR) break; - } - - if (r >= minR) { - // we have found a spot for this bubble to live, let's save it and move on - r = Math.min(maxR, r); - - mBubbs[mNumBubbs].x = x; - mBubbs[mNumBubbs].y = y; - mBubbs[mNumBubbs].r = r; - mBubbs[mNumBubbs].color = mColors[(int) (Math.random() * mColors.length)]; - mNumBubbs++; - break; - } - } - } - Log.v(TAG, String.format("successfully placed %d bubbles (%d%%)", - mNumBubbs, (int) (100f * mNumBubbs / MAX_BUBBS))); } @Override - public void setAlpha(int alpha) { } + public void setColorFilter(@Nullable ColorFilter colorFilter) { - @Override - public void setColorFilter(ColorFilter colorFilter) { } + } @Override public int getOpacity() { - return TRANSLUCENT; + return PixelFormat.OPAQUE; } - @Override - public boolean onLongClick(View v) { - if (getLevel() == 0) return false; - chooseEmojiSet(); - return true; + public void update(long dt) { + mDt = dt; } } - -} +}
\ No newline at end of file diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 8135f9cd2f46..dd52de4f84c7 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -118,6 +118,9 @@ public final class SystemUiDeviceConfigFlags { */ public static final String NAS_DEFAULT_SERVICE = "nas_default_service"; + /** (boolean) Whether notify() calls to NMS should acquire and hold WakeLocks. */ + public static final String NOTIFY_WAKELOCK = "nms_notify_wakelock"; + // Flags related to media notifications /** diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index 3d95dd341cc0..c9e76009136a 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -81,6 +81,10 @@ public class SystemUiSystemPropertiesFlags { /** Gating the logging of DND state change events. */ public static final Flag LOG_DND_STATE_EVENTS = releasedFlag("persist.sysui.notification.log_dnd_state_events"); + + /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ + public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = + devFlag("persist.sysui.notification.wake_lock_for_posting_notification"); } //// == End of flags. Everything below this line is the implementation. == //// diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java index 3ba4ea55b5d3..80d753c7ed09 100644 --- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java +++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java @@ -120,6 +120,10 @@ public class AnrLatencyTracker implements AutoCloseable { private long mPreDumpIfLockTooSlowStartUptime; private long mPreDumpIfLockTooSlowDuration = 0; + private long mNotifyAppUnresponsiveStartUptime; + private long mNotifyAppUnresponsiveDuration = 0; + private long mNotifyWindowUnresponsiveStartUptime; + private long mNotifyWindowUnresponsiveDuration = 0; private final int mAnrRecordPlacedOnQueueCookie = sNextAnrRecordPlacedOnQueueCookieGenerator.incrementAndGet(); @@ -425,11 +429,36 @@ public class AnrLatencyTracker implements AutoCloseable { anrSkipped("dumpStackTraces"); } + /** Records the start of AnrController#notifyAppUnresponsive. */ + public void notifyAppUnresponsiveStarted() { + mNotifyAppUnresponsiveStartUptime = getUptimeMillis(); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyAppUnresponsive()"); + } + + /** Records the end of AnrController#notifyAppUnresponsive. */ + public void notifyAppUnresponsiveEnded() { + mNotifyAppUnresponsiveDuration = getUptimeMillis() - mNotifyAppUnresponsiveStartUptime; + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + + /** Records the start of AnrController#notifyWindowUnresponsive. */ + public void notifyWindowUnresponsiveStarted() { + mNotifyWindowUnresponsiveStartUptime = getUptimeMillis(); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyWindowUnresponsive()"); + } + + /** Records the end of AnrController#notifyWindowUnresponsive. */ + public void notifyWindowUnresponsiveEnded() { + mNotifyWindowUnresponsiveDuration = getUptimeMillis() + - mNotifyWindowUnresponsiveStartUptime; + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + /** * Returns latency data as a comma separated value string for inclusion in ANR report. */ public String dumpAsCommaSeparatedArrayWithHeader() { - return "DurationsV4: " + mAnrTriggerUptime + return "DurationsV5: " + mAnrTriggerUptime /* triggering_to_app_not_responding_duration = */ + "," + (mAppNotRespondingStartUptime - mAnrTriggerUptime) /* app_not_responding_duration = */ @@ -480,6 +509,10 @@ public class AnrLatencyTracker implements AutoCloseable { + "," + (mCopyingFirstPidSucceeded ? 1 : 0) /* preDumpIfLockTooSlow_duration = */ + "," + mPreDumpIfLockTooSlowDuration + /* notifyAppUnresponsive_duration = */ + + "," + mNotifyAppUnresponsiveDuration + /* notifyWindowUnresponsive_duration = */ + + "," + mNotifyWindowUnresponsiveDuration + "\n\n"; } diff --git a/core/proto/android/companion/telecom.proto b/core/proto/android/companion/telecom.proto index 700baa1f4ee5..7fe2467032cb 100644 --- a/core/proto/android/companion/telecom.proto +++ b/core/proto/android/companion/telecom.proto @@ -45,10 +45,18 @@ message Telecom { AUDIO_PROCESSING = 5; RINGING_SIMULATED = 6; DISCONNECTED = 7; + DIALING = 8; } Status status = 3; repeated Control controls = 4; + + enum Direction { + UNKNOWN_DIRECTION = 0; + INCOMING = 1; + OUTGOING = 2; + } + Direction direction = 5; } message Request { diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index da214868ca05..f3acab063bbf 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -13,33 +13,186 @@ Copyright (C) 2021 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<vector android:height="128dp" - android:width="128dp" - android:viewportHeight="24" - android:viewportWidth="24" - xmlns:android="http://schemas.android.com/apk/res/android"> - <path - android:fillColor="@android:color/system_accent1_400" - android:pathData="M11,0.3c0.6,-0.3 1.4,-0.3 2,0l0.6,0.4c0.7,0.4 1.4,0.6 2.2,0.6l0.7,-0.1c0.7,0 1.4,0.3 1.8,0.9l0.3,0.6c0.4,0.7 1,1.2 1.7,1.5L21,4.5c0.7,0.3 1.1,0.9 1.2,1.7v0.7C22.2,7.7 22.5,8.4 23,9l0.4,0.5c0.4,0.6 0.5,1.3 0.2,2l-0.3,0.6c-0.3,0.7 -0.4,1.5 -0.3,2.3l0.1,0.7c0.1,0.7 -0.2,1.4 -0.7,1.9L22,17.5c-0.6,0.5 -1.1,1.1 -1.3,1.9L20.5,20c-0.2,0.7 -0.8,1.2 -1.5,1.4l-0.7,0.1c-0.8,0.2 -1.4,0.5 -2,1.1l-0.5,0.5c-0.5,0.5 -1.3,0.7 -2,0.5l-0.6,-0.2c-0.8,-0.2 -1.5,-0.2 -2.3,0l-0.6,0.2c-0.7,0.2 -1.5,0 -2,-0.5l-0.5,-0.5c-0.5,-0.5 -1.2,-0.9 -2,-1.1L5,21.4c-0.7,-0.2 -1.3,-0.7 -1.5,-1.4l-0.2,-0.7C3.1,18.6 2.6,18 2,17.5l-0.6,-0.4c-0.6,-0.5 -0.8,-1.2 -0.7,-1.9l0.1,-0.7c0.1,-0.8 0,-1.6 -0.3,-2.3l-0.3,-0.6c-0.3,-0.7 -0.2,-1.4 0.2,-2L1,9c0.5,-0.6 0.7,-1.4 0.8,-2.2V6.2C1.9,5.5 2.3,4.8 3,4.5l0.6,-0.3c0.7,-0.3 1.3,-0.9 1.7,-1.5l0.3,-0.6c0.4,-0.6 1.1,-1 1.8,-0.9l0.7,0.1c0.8,0 1.6,-0.2 2.2,-0.6L11,0.3z" - /> - <path - android:pathData="M6.3,6.5l3,0l0,12.2" - android:strokeWidth="2.22" - android:strokeColor="@android:color/system_accent3_800" - /> - <path - android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" - android:strokeWidth="2.22" - android:strokeColor="@android:color/system_accent3_800" - /> - <path - android:pathData="M6.3,6.5l3,0l0,12.2" - android:strokeWidth="0.56" - android:strokeColor="@android:color/system_neutral1_100" - /> - <path - android:pathData="M12.3,6.5h4l-2,4c2.2,0.3 3.6,2.4 3.3,4.5c-0.3,1.9 -1.9,3.3 -3.8,3.3c-0.5,0 -1,-0.1 -1.4,-0.3" - android:strokeWidth="0.56" - android:strokeColor="@android:color/system_neutral1_100" - /> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="512dp" + android:height="512dp" + android:viewportWidth="512" + android:viewportHeight="512"> + <path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"> + <aapt:attr name="android:fillColor"> + <gradient + android:startX="256" + android:startY="21.81" + android:endX="256" + android:endY="350.42" + android:type="linear"> + <item android:offset="0" android:color="#FF073042"/> + <item android:offset="1" android:color="#FF073042"/> + </gradient> + </aapt:attr> + </path> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="m195.27,187.64l2.25,-6.69c13.91,78.13 50.84,284.39 50.84,50.33 0,-0.97 0.72,-1.81 1.62,-1.81h12.69c0.9,0 1.62,0.83 1.62,1.8 -0.2,409.91 -69.03,-43.64 -69.03,-43.64Z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="m158.77,180.68l-33.17,57.45c-1.9,3.3 -0.77,7.52 2.53,9.42 3.3,1.9 7.52,0.77 9.42,-2.53l33.59,-58.17c54.27,24.33 116.34,24.33 170.61,0l33.59,58.17c1.97,3.26 6.21,4.3 9.47,2.33 3.17,-1.91 4.26,-5.99 2.47,-9.23l-33.16,-57.45c56.95,-30.97 95.91,-88.64 101.61,-156.76H57.17c5.7,68.12 44.65,125.79 101.61,156.76Z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M250.26,187.66L262.17,187.66A2.13,2.13 0,0 1,264.3 189.78L264.3,217.85A2.13,2.13 0,0 1,262.17 219.98L250.26,219.98A2.13,2.13 0,0 1,248.14 217.85L248.14,189.78A2.13,2.13 0,0 1,250.26 187.66z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M250.12,170.29L262.32,170.29A1.98,1.98 0,0 1,264.3 172.26L264.3,176.39A1.98,1.98 0,0 1,262.32 178.37L250.12,178.37A1.98,1.98 0,0 1,248.14 176.39L248.14,172.26A1.98,1.98 0,0 1,250.12 170.29z" + android:fillColor="#3ddc84"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M171.92,216.82h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M188.8,275.73h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M369.04,337.63h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M285.93,252.22h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M318.96,218.84h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M294.05,288.55h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M330.82,273.31h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M188.8,298.95h4.04v4.04h-4.04z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M220.14,238.94h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M272.1,318.9h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M293.34,349.25h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M161.05,254.24h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M378.92,192h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <group> + <clip-path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0"/> + <path + android:pathData="M137.87,323.7h8.08v8.08h-8.08z" + android:fillColor="#fff"/> + </group> + <path + android:pathData="M256,256m-200,0a200,200 0,1 1,400 0a200,200 0,1 1,-400 0" + android:strokeWidth="56.561" + android:fillColor="#00000000" + android:strokeColor="#f86734"/> + <path + android:pathData="m256.22,126.57c-6.69,0 -12.12,5.27 -12.12,11.77v14.52c0,1.25 1.02,2.27 2.27,2.27h0c1.25,0 2.27,-1.02 2.27,-2.27v-3.91c0,-2.51 2.04,-4.55 4.55,-4.55h6.06c2.51,0 4.55,2.04 4.55,4.55v3.91c0,1.25 1.02,2.27 2.27,2.27s2.27,-1.02 2.27,-2.27v-14.52c0,-6.5 -5.43,-11.77 -12.12,-11.77Z" + android:fillColor="#3ddc84"/> + <path + android:pathData="m93.34,116.36l3.85,-4.36 29.64,9.76 -4.44,5.03 -6.23,-2.1 -7.86,8.91 2.86,5.92 -4.43,5.03 -13.39,-28.18ZM110.43,122.76l-8.86,-3.02 4.11,8.41 4.76,-5.39Z" + android:fillColor="#fff"/> + <path + android:pathData="m153.62,100.85l-21.71,-6.2 10.38,14.38 -5.21,3.76 -16.78,-23.26 4.49,-3.24 21.65,6.19 -10.35,-14.35 5.24,-3.78 16.78,23.26 -4.49,3.24Z" + android:fillColor="#fff"/> + <path + android:pathData="m161.46,63.15l8.99,-3.84c7.43,-3.18 15.96,0.12 19.09,7.44 3.13,7.32 -0.38,15.76 -7.81,18.94l-8.99,3.84 -11.28,-26.38ZM179.41,80.26c4.46,-1.91 5.96,-6.72 4.15,-10.96 -1.81,-4.24 -6.33,-6.48 -10.79,-4.57l-3.08,1.32 6.64,15.53 3.08,-1.32Z" + android:fillColor="#fff"/> + <path + android:pathData="m204.23,47.57l11.1,-2.2c5.31,-1.05 9.47,2.08 10.4,6.76 0.72,3.65 -0.76,6.37 -4.07,8.34l12.4,10.44 -7.57,1.5 -11.65,-9.76 -1.03,0.2 2.3,11.61 -6.3,1.25 -5.57,-28.14ZM216.78,56.7c1.86,-0.37 3,-1.71 2.68,-3.33 -0.34,-1.7 -1.88,-2.43 -3.74,-2.06l-4.04,0.8 1.07,5.39 4.04,-0.8Z" + android:fillColor="#fff"/> + <path + android:pathData="m244.29,55.6c0.13,-8.16 6.86,-14.72 15.06,-14.58 8.16,0.13 14.72,6.9 14.58,15.06s-6.91,14.72 -15.06,14.58c-8.2,-0.13 -14.71,-6.9 -14.58,-15.06ZM267.44,55.98c0.08,-4.64 -3.54,-8.66 -8.18,-8.74 -4.68,-0.08 -8.42,3.82 -8.5,8.47 -0.08,4.65 3.54,8.66 8.22,8.74 4.64,0.08 8.39,-3.82 8.46,-8.47Z" + android:fillColor="#fff"/> + <path + android:pathData="m294.39,44.84l6.31,1.23 -5.49,28.16 -6.31,-1.23 5.49,-28.16Z" + android:fillColor="#fff"/> + <path + android:pathData="m321.94,51.41l9.14,3.48c7.55,2.88 11.39,11.17 8.56,18.61 -2.83,7.44 -11.22,11.07 -18.77,8.19l-9.14,-3.48 10.22,-26.8ZM322.96,76.19c4.53,1.73 8.95,-0.69 10.6,-5 1.64,-4.3 -0.05,-9.06 -4.58,-10.78l-3.13,-1.19 -6.01,15.78 3.13,1.19Z" + android:fillColor="#fff"/> + <path + android:pathData="m381.41,89.24l-4.21,-3.21 3.65,-4.78 9.06,6.91 -17.4,22.81 -4.85,-3.7 13.75,-18.02Z" + android:fillColor="#fff"/> + <path + android:pathData="m397.96,126.37l-9.56,-10.26 3.61,-3.36 22.8,-1.25 3.74,4.02 -12.35,11.51 2.51,2.69 -4.08,3.8 -2.51,-2.69 -4.55,4.24 -4.16,-4.46 4.55,-4.24ZM407.83,117.17l-10.28,0.58 4.49,4.82 5.79,-5.4Z" + android:fillColor="#fff"/> </vector> + diff --git a/core/res/res/layout/alert_dialog_button_bar_leanback.xml b/core/res/res/layout/alert_dialog_button_bar_leanback.xml index ea94af662dcf..466811f6d116 100644 --- a/core/res/res/layout/alert_dialog_button_bar_leanback.xml +++ b/core/res/res/layout/alert_dialog_button_bar_leanback.xml @@ -21,13 +21,13 @@ android:layout_height="wrap_content" android:scrollbarAlwaysDrawVerticalTrack="true" android:scrollIndicators="top|bottom" - android:fillViewport="true" - style="?attr/buttonBarStyle"> + android:fillViewport="true"> <com.android.internal.widget.ButtonBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layoutDirection="locale" android:orientation="horizontal" + style="?attr/buttonBarStyle" android:gravity="start"> <Button diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 33e7ec97d079..a6b1fdbc588d 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Jy kan nie op jou <xliff:g id="DEVICE">%1$s</xliff:g> toegang hiertoe kry nie. Probeer eerder op jou foon."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Hierdie program is vir ’n ouer weergawe van Android gebou. Dit sal dalk nie behoorlik werk nie en dit sluit nie die jongste sekuriteit en privaatheidbeskermings in nie. Kyk of daar ’n opdatering is of kontak die program se ontwikkelaar."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kyk vir opdatering"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Hierdie app is nie met die jongste weergawe van Android versoenbaar nie. Kyk of daar ’n opdatering is, of kontak die app se ontwikkelaar."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Jy het nuwe boodskappe"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Maak SMS-program oop om te bekyk"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Sommige funksies kan beperk wees"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 90137083e95f..f3896a3a8381 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ይህ በእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> ላይ ሊደረስበት አይችልም። በምትኩ በስልክዎ ላይ ይሞክሩ።"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ይህ መተግበሪያ የተገነባው ለቆየ የAndroid ስሪት ነበር። በትክክል ላይሰራ ይችላል እና የቅርብ ጊዜዎቹን የደህንነት እና የግላዊነት ጥበቃዎች አያካትትም። ዝማኔ ካለ ይፈትሹ ወይም የመተግበሪያውን ገንቢ ያነጋግሩ።"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ዝማኔ ካለ አረጋግጥ"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ይህ መተግበሪያ ከቅርብ ጊዜው የAndroid. ስሪት ጋር ተኳዃኝ አይደለም። ዝማኔ ካለ ይፈትሹ ወይም የመተግበሪያውን ገንቢ ያነጋግሩ።"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"አዲስ መልዕክቶች አለዎት"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"ለመመልከት የኤስኤምኤስ መተግበሪያ ይክፈቱ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"አንዳንድ ተግባሮች የተገደቡ ሊሆኑ ይችላሉ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index bcfdef20ffca..546713586c8a 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1979,6 +1979,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"لا يمكن الوصول إلى هذه الإعدادات على <xliff:g id="DEVICE">%1$s</xliff:g>. بدلاً من ذلك، جرِّب استخدام هاتفك."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"تم إنشاء هذا التطبيق لإصدار قديم من Android. قد لا يعمل بشكل صحيح كما أنه لا يشتمِل على أحدث الإجراءات لحماية الأمان والخصوصية. ابحث عن تحديث أو تواصَل مع مطوّر التطبيق."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"البحث عن تحديث"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"هذا التطبيق غير متوافق مع أحدث إصدار من Android. ابحث عن تحديث أو تواصَل مع مطوّر التطبيق."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"لديك رسائل جديدة"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"فتح تطبيق الرسائل القصيرة SMS للعرض"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"قد تكون بعض الوظائف مُقيّدة."</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index f8f390a7ce8e..123030175923 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"এইটো আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ত এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ ফ’নত চেষ্টা কৰি চাওক।"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"এই এপ্টো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে বনোৱা হৈছিল। এইটোৱে সঠিকভাৱে কাম নকৰিব পাৰে আৰু ইয়াত শেহতীয়া সুৰক্ষা আৰু গোপনীয়তা সম্পৰ্কীয় সুৰক্ষাসমূহ নাথাকে। কোনো আপডে’ট আছে নেকি চাওক অথবা এপৰ বিকাশকৰ্তাগৰাকীৰ সৈতে যোগাযোগ কৰক।"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"আপডে’ট আছে নেকি চাওক"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"এই এপ্টো Androidৰ শেহতীয়া সংস্কৰণটোৰ সৈতে সমিল নহয়। আপডে’ট আছে নেকি চাওক অথবা এপৰ বিকাশকৰ্তাগৰাকীৰ সৈতে যোগাযোগ কৰক।"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"আপুনি নতুন বার্তা লাভ কৰিছে"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"চাবলৈ এছএমএছ এপ্ খোলক"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"কিছুমান কাৰ্যকাৰিতা সীমিত হ’ব পাৰে"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 6e3a1cc992de..45b67ebabb2a 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızda buna giriş mümkün deyil. Telefonunuzda sınayın."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Bu tətbiq köhnə Android versiyası üçün nəzərdə tutulub. O, düzgün işləməyə bilər və ən son təhlükəsizlik və məxfilik qorumalarını ehtiva etmir. Güncəlləməni yoxlayın və ya tətbiq tərtibatçısı ilə əlaqə saxlayın."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncəllənmə olmasını yoxlayın"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Bu tətbiq son Android versiyası ilə uyğun deyil. Güncəllənməni yoxlayın və ya tətbiq tərtibatçısı ilə əlaqə saxlayın."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Baxmaq üçün SMS tətbiqini açın"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Bəzi funksiyalar məhdudlaşdırıla bilər"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index f68eab66e8dd..a310cfbad129 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -86,7 +86,7 @@ <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Probajte da promenite željenu mrežu. Dodirnite da biste promenili."</string> <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hitni pozivi nisu dostupni"</string> <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Ne možete da upućujete hitne pozive preko Wi‑Fi-ja"</string> - <string name="notification_channel_network_alert" msgid="4788053066033851841">"Obaveštenja"</string> + <string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozorenja"</string> <string name="notification_channel_call_forward" msgid="8230490317314272406">"Preusmeravanje poziva"</string> <string name="notification_channel_emergency_callback" msgid="54074839059123159">"Režim za hitan povratni poziv"</string> <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Status mobilnih podataka"</string> @@ -284,7 +284,7 @@ <string name="notification_channel_network_available" msgid="6083697929214165169">"Mreža je dostupna"</string> <string name="notification_channel_vpn" msgid="1628529026203808999">"Status VPN-a"</string> <string name="notification_channel_device_admin" msgid="6384932669406095506">"Obaveštenja od IT administratora"</string> - <string name="notification_channel_alerts" msgid="5070241039583668427">"Obaveštenja"</string> + <string name="notification_channel_alerts" msgid="5070241039583668427">"Upozorenja"</string> <string name="notification_channel_retail_mode" msgid="3732239154256431213">"Režim demonstracije za maloprodajne objekte"</string> <string name="notification_channel_usb" msgid="1528280969406244896">"USB veza"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Aktivna aplikacija"</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ovoj aplikaciji ne može da se pristupi sa uređaja <xliff:g id="DEVICE">%1$s</xliff:g>. Probajte na telefonu."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova aplikacija je napravljena za stariju verziju Android-a. Možda neće raditi ispravno i ne obuhvata najnovije bezbednosne funkcije i zaštite privatnosti. Proverite da li ima ažuriranja ili se obratite programeru aplikacije."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Potraži ažuriranje"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ova aplikacija nije kompatibilna sa najnovijom verzijom Android-a. Proverite da li ima ažuriranja ili se obratite programeru aplikacije."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite aplikaciju za SMS da biste pregledali"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Neke funkcije su možda ograničene"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index a47c5b9bfdca..7e24c7bfc9c6 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Не ўдаецца атрымаць доступ з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\". Паспрабуйце скарыстаць тэлефон."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Гэта праграма створана для ранейшай версіі Android. Яна можа кепска працаваць, і ў ёй няма новых сродкаў абароны бяспекі і прыватнасці. Праверце наяўнасць абнаўленняў ці звярніцеся да распрацоўшчыка праграмы."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Праверыць наяўнасць абнаўленняў"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Гэтая праграма несумяшчальная з апошняй версіяй Android. Праверце наяўнасць абнаўленняў ці звярніцеся да распрацоўшчыка праграмы."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"У вас ёсць новыя паведамленні"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Праглядзець праз праграму для SMS"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Функцыі могуць быць абмежаваныя"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 4ed4a6601292..d2afdb2981bb 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Не може да се осъществи достъп от устройството ви <xliff:g id="DEVICE">%1$s</xliff:g>. Вместо това опитайте от телефона си."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Това приложение бе създадено за по-стара версия на Android. То може да не работи правилно и не включва най-новите защити на поверителността и сигурността. Проверете за актуализация или се свържете с програмиста му."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверка за актуализация"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Това приложение не е съвместимо с най-новата версия на Android. Проверете за актуализация или се свържете с програмиста му."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нови съобщения"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Преглед в приложението за SMS"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Някои функции може да са ограничени"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 82532fbd5caa..42c00f4b043b 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g>-এ এটি অ্যাক্সেস করা যাবে না। পরিবর্তে আপনার ফোনে ব্যবহার করে দেখুন।"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Android-এর আরও পুরনো ভার্সনের জন্য এই অ্যাপ তৈরি করা হয়েছিল। এটি সঠিকভাবে কাজ নাও করতে পারে এবং এর মধ্যে লেটেস্ট সুরক্ষা ও গোপনীয়তার নিরাপত্তা ব্যবস্থা অন্তর্ভুক্ত করা নেই। আপডেটের জন্য চেক করুন বা অ্যাপের ডেভেলপারের সাথে যোগাযোগ করুন।"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"আপডেট পাওয়া যাচ্ছে কিনা দেখুন"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Android-এর লেটেস্ট ভার্সনের সাথে এই অ্যাপটি মানানসই নয়। আপডেটের জন্য চেক করুন বা অ্যাপ ডেভেলপারের সাথে যোগাযোগ করুন।"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"আপনার নতুন মেসেজ আছে"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"দেখার জন্য SMS অ্যাপ্লিকেশান খুলুন"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"কিছু ফাংশন হয়ত কাজ করবে না"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index c625a3a41864..3e5700493c1b 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -995,8 +995,8 @@ <string name="lockscreen_sim_locked_message" msgid="5911944931911850164">"SIM je zaključan."</string> <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Otključavanje SIM-a…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Pogrešno ste nacrtali svoj uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string> - <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Pogrešno ste unijeli svoju lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Pogrešno ste unijeli svoj PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string> + <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Pogrešno ste napisali svoju lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Pogrešno ste napisali svoj PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Pogrešno ste unijeli uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Možete pokušati još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta. Nakon toga ćete morati otključati tablet prijavom na svoj Google račun.\n\n Broj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_2">%3$d</xliff:g>"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Pogrešno ste unijeli uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> put(a). U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, morat ćete otključati Android TV uređaj prijavom na svoj Google račun.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Pogrešno ste unijeli uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Možete pokušati još <xliff:g id="NUMBER_1">%2$d</xliff:g> puta. Nakon toga ćete morati otključati telefon prijavom na svoj Google račun.\n\n Broj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_2">%3$d</xliff:g>"</string> @@ -1668,8 +1668,8 @@ <string name="kg_login_invalid_input" msgid="8292367491901220210">"Pogrešno korisničko ime ili lozinka."</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Zaboravili ste korisničko ime ili lozinku?\nPosjetite "<b>"google.com/accounts/recovery"</b>"."</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"Provjeravanje računa…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Pogrešno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Pogrešno ste unijeli lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Pogrešno ste napisali PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Pogrešno ste napisali lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Pogrešno ste nacrtali uzorak <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nBroj sekundi do sljedećeg pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Pokušali ste otključati tablet na pogrešan način <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ako napravite još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, tablet će biti vraćen na fabričke postavke i svi korisnički podaci će biti izgubljeni."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Pokušali ste neispravno otključati Android TV uređaj <xliff:g id="NUMBER_0">%1$d</xliff:g> put(a). U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, vaš Android TV će se vratiti na fabričke postavke i svi korisnički podaci će biti izgubljeni."</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ne možete pristupiti ovoj aplikaciji na uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Umjesto toga pokušajte na telefonu."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova aplikacija je izrađena za stariju verziju Androida. Možda neće pravilno funkcionirati i ne sadržava najnovije sigurnosne zaštite i zaštite privatnosti. Provjerite ima li ažuriranja ili se obratite programeru aplikacije."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Provjeri je li dostupno ažuriranje"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Aplikacija nije kompatibilna s najnovijom verzijom Androida. Provjerite ima li ažuriranja ili se obratite programeru aplikacije."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite SMS aplikaciju da biste pregledali poruke"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Neke funkcije mogu biti ograničene"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 4e22a1557f9a..622d648943a3 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -702,7 +702,7 @@ <!-- no translation found for face_acquired_mouth_covering_detected (8219428572168642593) --> <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"No es pot crear el model facial. Torna-ho a provar."</string> - <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"S\'han detectat ulleres fosques. La cara ha de ser completament visible."</string> + <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"S\'han detectat ulleres fosques. La cara ha de veure\'s sencera."</string> <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"S\'ha detectat una mascareta. La cara ha de veure\'s sencera."</string> <string-array name="face_acquired_vendor"> </string-array> @@ -996,7 +996,7 @@ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"S\'està desbloquejant la targeta SIM…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis la tauleta amb l\'inici de sessió de Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis el dispositiu Android TV iniciant la sessió a Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. Si falles <xliff:g id="NUMBER_1">%2$d</xliff:g> vegades més, se\'t demanarà que desbloquegis el telèfon amb l\'inici de sessió de Google.\n\n Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%3$d</xliff:g> segons."</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No s\'hi pot accedir des del teu <xliff:g id="DEVICE">%1$s</xliff:g>. Prova-ho al telèfon."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Aquesta aplicació es va compilar per a una versió anterior d\'Android. Pot ser que no funcioni correctament i no inclou les darreres proteccions de seguretat i privadesa. Comprova si hi ha actualitzacions o contacta amb el desenvolupador de l\'aplicació."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Cerca actualitzacions"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Aquesta aplicació no és compatible amb la darrera versió d\'Android. Comprova si hi ha actualitzacions o contacta amb el desenvolupador de l\'aplicació."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Tens missatges nous"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Obre l\'aplicació d\'SMS per veure\'ls"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Algunes funcions poden ser limitades"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 83589c3dcd44..01f4572345b6 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Tato položka na vašem zařízení <xliff:g id="DEVICE">%1$s</xliff:g> není k dispozici. Zkuste to na telefonu."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Tato aplikace byla vytvořena pro starší verzi Androidu. Nemusí fungovat správně a neobsahuje nejnovější zabezpečení a ochranu soukromí. Zkontrolujte dostupnost aktualizace nebo kontaktujte vývojáře aplikace."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Zkontrolovat aktualizace"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Tato aplikace není kompatibilní s poslední verzí Androidu. Zkontrolujte dostupnost aktualizace nebo kontaktujte vývojáře aplikace."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Máte nové zprávy"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Zobrazíte je v aplikaci pro SMS"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Některé funkce mohou být omezeny"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index c845e72ae1d3..ebd38c89cfcf 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -684,7 +684,7 @@ <string name="face_acquired_too_high" msgid="8278815780046368576">"Løft telefonen højere op"</string> <string name="face_acquired_too_low" msgid="4075391872960840081">"Sænk telefonen"</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"Flyt telefonen længere til venstre for dig"</string> - <string name="face_acquired_too_left" msgid="9201762240918405486">"Flyt telefonen længere til højre for dig"</string> + <string name="face_acquired_too_left" msgid="9201762240918405486">"Flyt telefonen længere til højre"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Kig mere direkte på din enhed."</string> <string name="face_acquired_not_detected" msgid="1057966913397548150">"Dit ansigt kan ikke registreres. Hold din telefon i øjenhøjde."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Der er for meget bevægelse. Hold telefonen stille."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Du kan ikke gøre dette på din <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på din telefon i stedet."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Denne app er udviklet til en ældre version af Android. Den fungerer muligvis ikke korrekt, og den omfatter ikke de nyeste sikkerhedsfunktioner og den nyeste privatlivsbeskyttelse. Tjek, om der er en opdatering, eller kontakt appudvikleren."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Søg efter opdatering"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Denne app er ikke kompatibel med den nyeste Android-version. Tjek, om der er en opdatering, eller kontakt appudvikleren."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nye beskeder"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Åbn sms-appen for at se beskeden"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Nogle funktioner er begrænsede"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 8576cb79cecd..4441580842b4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1950,7 +1950,7 @@ <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ist momentan nicht verfügbar. Dies wird über die App \"<xliff:g id="APP_NAME_1">%2$s</xliff:g>\" verwaltet."</string> <string name="app_suspended_more_details" msgid="211260942831587014">"Weitere Informationen"</string> <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"App-Pausierung aufheben"</string> - <string name="work_mode_off_title" msgid="6367463960165135829">"Geschäftl. Apps nicht mehr pausieren?"</string> + <string name="work_mode_off_title" msgid="6367463960165135829">"Geschäftliche Apps nicht mehr pausieren?"</string> <string name="work_mode_turn_on" msgid="5316648862401307800">"Nicht mehr pausieren"</string> <string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Notruf"</string> <string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Auf deinem <xliff:g id="DEVICE">%1$s</xliff:g> ist kein Zugriff möglich. Versuch es stattdessen auf deinem Smartphone."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Diese App wurde für eine ältere Android-Version entwickelt. Sie funktioniert möglicherweise nicht richtig und enthält nicht die neuesten Sicherheits- und Datenschutzeinstellungen. Suche nach einem Update oder wende dich an den App-Entwickler."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Auf Updates prüfen"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Diese App ist nicht mit der neuesten Android-Version kompatibel. Suche nach einem Update oder wende dich an den App-Entwickler."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Du hast neue Nachrichten"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Zum Ansehen SMS-App öffnen"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Funktionen sind evtl. eingeschränkt"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index a39e988bd773..7a0f5c0520e9 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>. Δοκιμάστε στο τηλέφωνό σας."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Αυτή η εφαρμογή σχεδιάστηκε για μια παλαιότερη έκδοση του Android. Ενδέχεται να μην λειτουργεί σωστά και δεν περιλαμβάνει τις πιο πρόσφατες λειτουργίες ασφάλειας και προστασίας απορρήτου. Ελέγξτε αν υπάρχει διαθέσιμη μια ενημέρωση ή επικοινωνήστε με τον προγραμματιστή της εφαρμογής."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Έλεγχος για ενημέρωση"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Αυτή η εφαρμογή δεν είναι συμβατή με την πιο πρόσφατη έκδοση Android. Ελέγξτε αν υπάρχει διαθέσιμη μια ενημέρωση ή επικοινωνήστε με τον προγραμματιστή της εφαρμογής."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Έχετε νέα μηνύματα"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Άνοιγμα της εφαρμογής SMS για προβολή"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Ορισμ. λειτ. ίσως είναι περιορισμ."</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index b43dfb9d6d7e..725f9fa3d00a 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"This app isn\'t compatible with the latest version of Android. Check for an update or contact the app\'s developer."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Some functionality may be limited"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 13b1c93333a2..86e1267eca9d 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update, or contact the app\'s developer."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"This app isn\'t compatible with the latest version of Android. Check for an update or contact the app\'s developer."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Some functionality may be limited"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 623f8293dd77..f0af9de9e6a9 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"This app isn\'t compatible with the latest version of Android. Check for an update or contact the app\'s developer."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Some functionality may be limited"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index e79afd5870d7..2677a55b7aeb 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update or contact the app\'s developer."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"This app isn\'t compatible with the latest version of Android. Check for an update or contact the app\'s developer."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Some functionality may be limited"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index a908198bfc80..fc93b3cccc43 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"This can’t be accessed on your <xliff:g id="DEVICE">%1$s</xliff:g>. Try on your phone instead."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"This app was built for an older version of Android. It might not work properly and doesn\'t include the latest security and privacy protections. Check for an update, or contact the app\'s developer."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"This app isn\'t compatible with the latest version of Android. Check for an update or contact the app\'s developer."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Open SMS app to view"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Some functionality may be limited"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 433ba3851600..2bf0e3618c5c 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -692,10 +692,10 @@ <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu rostro."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"No se reconoce el rostro. Vuelve a intentarlo."</string> <string name="face_acquired_too_similar" msgid="8882920552674125694">"Cambia levemente la posición de la cabeza"</string> - <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Mira el teléfono de forma más directa"</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Mira el teléfono de forma más directa"</string> - <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Mira el teléfono de forma más directa"</string> - <string name="face_acquired_obscured" msgid="4917643294953326639">"Quítate cualquier objeto que te cubra el rostro."</string> + <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Mira directamente al teléfono"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Mira directamente al teléfono"</string> + <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Mira directamente al teléfono"</string> + <string name="face_acquired_obscured" msgid="4917643294953326639">"Quítate cualquier objeto que te cubra el rostro"</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"Limpia la parte superior de la pantalla, incluida la barra negra"</string> <!-- no translation found for face_acquired_dark_glasses_detected (5643703296620631986) --> <skip /> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No se puede acceder a esto en tu <xliff:g id="DEVICE">%1$s</xliff:g>. Inténtalo en tu teléfono."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Esta app se compiló para una versión anterior de Android. Es posible que no funcione correctamente ni incluya las protecciones de la privacidad más recientes. Consulta si hay actualizaciones o comunícate con el desarrollador."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualización"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Esta app no es compatible con la versión más reciente de Android. Revisa si hay actualizaciones o comunícate con el desarrollador."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Tienes mensajes nuevos"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Abrir app de SMS para ver el mensaje"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Podrían limitarse algunas funciones"</string> @@ -2324,7 +2325,7 @@ <string name="mic_access_on_toast" msgid="2666925317663845156">"El micrófono está disponible"</string> <string name="mic_access_off_toast" msgid="8111040892954242437">"El micrófono está bloqueado"</string> <string name="concurrent_display_notification_name" msgid="1526911253558311131">"Pantalla dual"</string> - <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La Pantalla dual está activada"</string> + <string name="concurrent_display_notification_active_title" msgid="4892473462327943673">"La función Dual Screen está activada"</string> <string name="concurrent_display_notification_active_content" msgid="5889355473710601270">"<xliff:g id="APP_NAME">%1$s</xliff:g> está usando ambas pantallas para mostrar contenido"</string> <string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"El dispositivo está muy caliente"</string> <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"La Pantalla dual no está disponible porque el teléfono se está calentando demasiado"</string> @@ -2336,7 +2337,7 @@ <string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"Diseño de teclado establecido en <xliff:g id="LAYOUT_1">%s</xliff:g>. Presiona para cambiar esta opción."</string> <string name="keyboard_layout_notification_two_selected_message" msgid="1876349944065922950">"Diseño de teclado establecido en <xliff:g id="LAYOUT_1">%1$s</xliff:g> y <xliff:g id="LAYOUT_2">%2$s</xliff:g>. Presiona para cambiar esta opción."</string> <string name="keyboard_layout_notification_three_selected_message" msgid="280734264593115419">"Diseño de teclado establecido en <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> y <xliff:g id="LAYOUT_3">%3$s</xliff:g>. Presiona para cambiar esta opción."</string> - <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Diseño de teclado establecido en <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> y <xliff:g id="LAYOUT_3">%3$s</xliff:g>. Presiona para cambiar esta opción."</string> + <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Diseño de teclado establecido en <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> y <xliff:g id="LAYOUT_3">%3$s</xliff:g>. Presiona para cambiarlo."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclados físicos configurados"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Presiona para ver los teclados"</string> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 03a12a0d7be4..69128d9bdd48 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -703,7 +703,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"No se puede crear tu modelo. Inténtalo de nuevo."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Gafas oscuras detectadas. Tu cara se debe poder ver entera."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Mascarilla detectada. Tu cara se debe poder ver entera."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Cara parcialmente cubierta. Tu cara se debe poder ver entera."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"No se puede verificar. Hardware no disponible."</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"No se puede acceder desde tu <xliff:g id="DEVICE">%1$s</xliff:g>. Prueba en tu teléfono."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Esta aplicación se creó para una versión anterior de Android. Puede que no funcione correctamente y que no incluya las protecciones de seguridad y privacidad más recientes. Comprueba si hay actualizaciones o ponte en contacto con el desarrollador de la aplicación."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualizaciones"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Esta aplicación no es compatible con la última versión de Android. Comprueba si hay actualizaciones o contacta con el desarrollador de la aplicación."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Tienes mensajes nuevos"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Abre la aplicación de SMS para ver el mensaje"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Algunas funciones limitadas"</string> @@ -2166,8 +2167,8 @@ <string name="miniresolver_open_work" msgid="6286176185835401931">"¿Abrir <xliff:g id="APP">%s</xliff:g> de trabajo?"</string> <string name="miniresolver_open_in_personal" msgid="807427577794490375">"¿Abrir en <xliff:g id="APP">%s</xliff:g> personal?"</string> <string name="miniresolver_open_in_work" msgid="941341494673509916">"¿Abrir en <xliff:g id="APP">%s</xliff:g> de trabajo?"</string> - <string name="miniresolver_call_in_work" msgid="528779988307529039">"¿Llamar desde una aplicación de trabajo?"</string> - <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"¿Cambiar a una aplicación de trabajo?"</string> + <string name="miniresolver_call_in_work" msgid="528779988307529039">"¿Llamar desde la aplicación de trabajo?"</string> + <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"¿Cambiar a la aplicación de trabajo?"</string> <string name="miniresolver_call_information" msgid="6739417525304184083">"Tu organización solo te permite hacer llamadas desde aplicaciones de trabajo"</string> <string name="miniresolver_sms_information" msgid="4311292661329483088">"Tu organización solo te permite enviar mensajes desde aplicaciones de trabajo"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Usar navegador personal"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 0328a05c70d9..0197f21e2b57 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1696,7 +1696,7 @@ <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"SEES"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"VÄLJAS"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Kas anda teenusele <xliff:g id="SERVICE">%1$s</xliff:g> teie seadme üle täielik kontroll?"</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"Täielik haldusõigus sobib rakendustele, mis pakuvad juurdepääsufunktsioone. Enamiku rakenduste puhul seda ei soovitata."</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"Täielik kontroll sobib rakendustele, mis pakuvad juurdepääsufunktsioone. Enamiku rakenduste puhul seda ei soovitata."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ekraanikuva vaatamine ja haldamine"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"See saab lugeda kogu ekraanil kuvatud sisu ja kuvada sisu rakenduste peal."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Toimingute vaatamine ja tegemine"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Sellele ei pääse teie seadmes <xliff:g id="DEVICE">%1$s</xliff:g> juurde. Proovige juurde pääseda oma telefonis."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"See rakendus on loodud Androidi vanema versiooni jaoks. See ei pruugi õigesti töötada ega hõlma uusimaid turva- ja privaatsusfunktsioone. Otsige värskendust või võtke ühendust rakenduse arendajaga."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Otsi värskendust"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"See rakendus ei ühildu Androidi uusima versiooniga. Otsige värskendust või võtke ühendust rakenduse arendajaga."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Teile on uusi sõnumeid"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Avage vaatamiseks SMS-rakendus"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Mõned funkts. võivad olla piiratud"</string> @@ -2167,11 +2168,11 @@ <string name="miniresolver_open_in_work" msgid="941341494673509916">"Kas avada töörakenduses <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"Kas helistada töörakendusest?"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"Kas lülituda töörakendusele?"</string> - <string name="miniresolver_call_information" msgid="6739417525304184083">"Teie organisatsioon lubab helistada ainult töörakendustest"</string> + <string name="miniresolver_call_information" msgid="6739417525304184083">"Teie organisatsioon lubab helistada ainult töörakendustest."</string> <string name="miniresolver_sms_information" msgid="4311292661329483088">"Teie organisatsioon lubab sõnumeid saata ainult töörakendustest."</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Kasuta isiklikku brauserit"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Kasuta tööbrauserit"</string> - <string name="miniresolver_call" msgid="6386870060423480765">"Helistage"</string> + <string name="miniresolver_call" msgid="6386870060423480765">"Helista"</string> <string name="miniresolver_switch" msgid="8011924662117617451">"Lülitu"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIM-kaardi võrgu avamise PIN-kood"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"SIM-kaardi võrgu alamhulga avamise PIN-kood"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 1fd274f48cf5..f14a0070177f 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -47,8 +47,8 @@ <string name="needPuk2" msgid="3910763547447344963">"Idatzi PUK2-a SIMa desblokeatzeko."</string> <string name="enablePin" msgid="2543771964137091212">"Ezin izan da aldatu. Gaitu SIM edo RUIM txartelaren blokeoa."</string> <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584"> - <item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela blokeatu aurretik.</item> - <item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu SIM txartela blokeatu aurretik.</item> + <item quantity="other"><xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIMa blokeatu aurretik.</item> + <item quantity="one"><xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu SIMa blokeatu aurretik.</item> </plurals> <string name="imei" msgid="2157082351232630390">"IMEIa"</string> <string name="meid" msgid="3291227361605924674">"MEID"</string> @@ -80,7 +80,7 @@ <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Ez dago ahots-deien zerbitzurik"</string> <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Ez dago ahozko zerbitzurik eta ezin da egin larrialdi-deirik"</string> <string name="RestrictedStateContent" msgid="7693575344608618926">"Operadoreak desaktibatu egin du aldi baterako"</string> - <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"Operadoreak <xliff:g id="SIMNUMBER">%d</xliff:g> SIM txartela desaktibatu egin du aldi baterako"</string> + <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"Operadoreak <xliff:g id="SIMNUMBER">%d</xliff:g> SIMa desaktibatu egin du aldi baterako"</string> <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Ezin da konektatu sare mugikorrera"</string> <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Aldatu sare hobetsia. Sakatu aldatzeko."</string> <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Ezin da egin larrialdi-deirik"</string> @@ -1044,7 +1044,7 @@ <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Aurpegi bidez desblokeatzea."</string> <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"PIN kodearen bidez desblokeatzea."</string> <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIMa desblokeatzeko PINa."</string> - <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM txartela desblokeatzeko PUK kodea."</string> + <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIMa desblokeatzeko PUK kodea."</string> <string name="keyguard_accessibility_password_unlock" msgid="6130186108581153265">"Pasahitzaren bidez desblokeatzea."</string> <string name="keyguard_accessibility_pattern_area" msgid="1419570880512350689">"Eredua marrazteko eremua."</string> <string name="keyguard_accessibility_slide_area" msgid="4331399051142520176">"Hatza lerratzeko eremua."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Aplikazioa ezin da <xliff:g id="DEVICE">%1$s</xliff:g> erabilita atzitu. Gailu horren ordez, erabili telefonoa."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Android-en bertsio zaharrago baterako sortu da aplikazio hau. Baliteke behar bezala ez funtzionatzea, eta ez ditu barne hartzen azken segurtasun- eta pribatutasun-babesak. Begiratu ea eguneratzerik dagoen edo jarri aplikazioaren garatzailearekin harremanetan."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Bilatu eguneratzeak"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Aplikazio hau ez da Androiden azken bertsioarekin bateragarria. Begiratu ea eguneratzerik dagoen edo jarri aplikazioaren garatzailearekin harremanetan."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Mezu berriak dituzu"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Mezuak ikusteko, ireki SMSetarako aplikazioa"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Baliteke funtzio batzuk mugatuta egotea"</string> @@ -2044,14 +2045,14 @@ <string name="etws_primary_default_message_test" msgid="4583367373909549421">"Larrialdi-mezuen proba"</string> <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Erantzun"</string> <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string> - <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM txartela ezin da erabili ahotsa erabiltzeko"</string> - <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"SIM txartela ez dago hornituta ahotsa erabiltzeko"</string> + <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIMa ezin da erabili ahotsa erabiltzeko"</string> + <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"SIMa ez dago hornituta ahotsa erabiltzeko"</string> <string name="mmcc_illegal_ms" msgid="7509650265233909445">"SIM txartela ezin da erabili ahotsa erabiltzeko"</string> <string name="mmcc_illegal_me" msgid="6505557881889904915">"Telefonoa ezin da erabili ahotsa erabiltzeko"</string> - <string name="mmcc_authentication_reject_msim_template" msgid="4480853038909922153">"Ezin da erabili <xliff:g id="SIMNUMBER">%d</xliff:g> SIM txartela"</string> + <string name="mmcc_authentication_reject_msim_template" msgid="4480853038909922153">"Ez da onartzen <xliff:g id="SIMNUMBER">%d</xliff:g> SIMa"</string> <string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="3688508325248599657">"Ez dago <xliff:g id="SIMNUMBER">%d</xliff:g> SIMik"</string> - <string name="mmcc_illegal_ms_msim_template" msgid="832644375774599327">"Ezin da erabili <xliff:g id="SIMNUMBER">%d</xliff:g> SIM txartela"</string> - <string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"Ezin da erabili <xliff:g id="SIMNUMBER">%d</xliff:g> SIM txartela"</string> + <string name="mmcc_illegal_ms_msim_template" msgid="832644375774599327">"Ezin da erabili <xliff:g id="SIMNUMBER">%d</xliff:g> SIMa"</string> + <string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"Ezin da erabili <xliff:g id="SIMNUMBER">%d</xliff:g> SIMa"</string> <string name="popup_window_default_title" msgid="6907717596694826919">"Leiho gainerakorra"</string> <string name="slice_more_content" msgid="3377367737876888459">"Beste <xliff:g id="NUMBER">%1$d</xliff:g>"</string> <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"Aplikazioaren bertsio zaharrago batera aldatu da, edo aplikazioa ez da lasterbide honekin bateragarria"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 415dce575740..74332ecc5439 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"نمیتوان در <xliff:g id="DEVICE">%1$s</xliff:g> به این مورد دسترسی داشت. دسترسی به آن را در تلفنتان امتحان کنید."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"این برنامه برای نسخه قدیمیتری از Android ساخته شده است. احتمال دارد بهدرستی کار نکند و شامل جدیدترین محافظتهای حریم خصوصی و امنیت نمیشود. بررسی کنید بهروزرسانی دردسترس باشد یا با توسعهدهنده برنامه تماس بگیرید."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"بررسی وجود بهروزرسانی"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"این برنامه با جدیدترین نسخه Android سازگار نیست. بررسی کنید بهروزرسانی دردسترس باشد یا با توسعهدهنده برنامه تماس بگیرید."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"پیامهای جدیدی دارید"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"برای مشاهده، برنامه پیامک را باز کنید"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"برخی از عملکردها ممکن است محدود باشند"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index ccca9df10fac..a5a0a3f7d9b3 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g> ei saa pääsyä sovellukseen. Kokeile striimausta puhelimella."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Tämä sovellus on suunniteltu vanhemmalle Android-versiolle. Se ei välttämättä toimi oikein eikä sisällä uusimpia tietoturvan ja yksityisyyden suojauksia. Tarkista päivitykset tai ota yhteyttä sovelluksen kehittäjään."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tarkista päivitykset"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Sovellus ei ole yhteensopiva uusimman Android-version kanssa. Tarkista päivitykset tai ota yhteyttä sovelluksen kehittäjään."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Sinulle on uusia viestejä"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Katso avaamalla tekstiviestisovellus."</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Osaa toiminnoista voidaan rajoittaa"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 97bbfb41533e..da9399298d18 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Impossible d\'accéder à ce contenu sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez sur votre téléphone à la place."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Cette application a été conçue pour une ancienne version d\'Android. Elle pourrait ne pas fonctionner correctement, et ne comprend pas les dernières protections des données confidentielles et de sécurité. Vérifiez s\'il existe une mise à jour ou contactez le développeur de l\'application."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Vérifier la présence de mises à jour"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Cette application n\'est pas compatible avec la dernière version d\'Android. Vérifiez s\'il existe une mise à jour ou communiquez avec le développeur de l\'application."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Vous avez de nouveaux messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Ouvrez l\'application de messagerie texte pour l\'afficher"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Des fonctionnalités sont limitées"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 6e2e1f09b3d6..f80373d07f01 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1698,9 +1698,9 @@ <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DÉSACTIVÉE"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Accorder le contrôle total de votre appareil à <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"Le contrôle total convient aux applications qui répondent à vos besoins d\'accessibilité. Il ne convient pas à la plupart des autres applications."</string> - <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Afficher et contrôler l\'écran"</string> + <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Voir et contrôler l\'écran"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Le service peut lire l\'intégralité du contenu à l\'écran et afficher du contenu par-dessus d\'autres applications."</string> - <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Afficher et effectuer des actions"</string> + <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Voir et effectuer des actions"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Le service peut suivre vos interactions avec une application ou un capteur matériel, et interagir avec des applications de votre part."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Autoriser"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuser"</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Impossible d\'accéder à ces paramètres sur votre <xliff:g id="DEVICE">%1$s</xliff:g>. Essayez plutôt sur votre téléphone."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Cette appli a été conçue pour une ancienne version d\'Android. Elle risque de ne pas fonctionner correctement et n\'inclut pas les derniers dispositifs de sécurité et de protection de la confidentialité. Recherchez une mise à jour ou contactez le développeur de l\'appli."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Rechercher une mise à jour"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Cette appli n\'est pas compatible avec la dernière version d\'Android. Recherchez une mise à jour ou contactez le développeur de l\'appli."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Vous avez de nouveaux messages"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Ouvrir l\'application de SMS pour afficher le message"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Des fonctions peuvent être limitées"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 9da299c02912..7aa45be7d126 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -702,7 +702,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Non se puido crear o modelo facial. Téntao de novo."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Levas lentes escuras, pero débeseche ver toda a cara"</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Levas máscara, pero débeseche ver toda a cara"</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Tes a cara tapada, pero débeseche ver enteira"</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Sen verificar a cara. Hardware non dispoñible."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Non se puido acceder a este contido desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>). Proba a facelo desde o teléfono."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Esta aplicación deseñouse para unha versión anterior de Android. Quizais non funcione correctamente e non inclúa as últimas medidas de protección de privacidade e seguranza. Comproba se hai actualizacións ou ponte en contacto co programador da aplicación."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualización"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Esta aplicación non é compatible coa última versión de Android. Comproba se hai actualizacións ou ponte en contacto co programador da aplicación."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Tes mensaxes novas"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Abre a aplicación de SMS para ver as mensaxes"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Pode haber funcións limitadas"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 1c7d98647f6c..9bc39626d133 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"આને તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પર ઍક્સેસ કરી શકાતી નથી. તેના બદલે તમારા ફોન પર પ્રયાસ કરો."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Androidના કોઈ જૂના વર્ઝન માટે આ ઍપ બનાવવામાં આવી હતી. તે કદાચ યોગ્ય રીતે કામ કરતી નથી અને તેમાં નવીનતમ સુરક્ષા અને પ્રાઇવસી સંબંધિત સંરક્ષણો શામેલ નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"અપડેટ માટે તપાસો"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"આ ઍપ Androidના નવીનતમ વર્ઝન સાથે સુસંગત નથી. કોઈ અપડેટ ચેક કરો અથવા ઍપના ડેવલપરનો સંપર્ક કરો."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા સંદેશા છે"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"જોવા માટે SMS ઍપ્લિકેશન ખોલો"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"કેટલીક કાર્યક્ષમતા મર્યાદિત હોઈ શકે છે"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index c750d50fb729..9ebe4f70c3e1 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> पर इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने फ़ोन पर ऐक्सेस करने की कोशिश करें."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"यह ऐप्लिकेशन, Android के पुराने वर्शन के लिए बनाया गया था. इसमें सिक्योरिटी और निजता सुरक्षा से जुड़ी नई सुविधाएं शामिल नहीं हैं. साथ ही, ऐसा हो सकता है कि यह ठीक से काम न करे. अपडेट के बारे में पता करें या ऐप्लिकेशन के डेवलपर से संपर्क करें."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"देखें कि अपडेट मौजूद है या नहीं"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"यह ऐप्लिकेशन, Android के सबसे नए वर्शन पर काम नहीं करता है. अपडेट के बारे में पता करें या ऐप्लिकेशन के डेवलपर से संपर्क करें."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"आपके पास नए संदेश हैं"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"देखने के लिए मैसेज (एसएमएस) ऐप खोलें"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"शायद कुछ सुविधाएं काम न करें"</string> @@ -2166,7 +2167,7 @@ <string name="miniresolver_open_in_personal" msgid="807427577794490375">"निजी प्रोफ़ाइल वाले <xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन में जाकर खोलें?"</string> <string name="miniresolver_open_in_work" msgid="941341494673509916">"वर्क प्रोफ़ाइल वाले <xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन में जाकर खोलें?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"क्या आपको वर्क ऐप्लिकेशन से कॉल करना है?"</string> - <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"क्या आपको वर्क ऐप्लिकेशन में स्विच करना है?"</string> + <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"क्या आपको वर्क ऐप्लिकेशन पर स्विच करना है?"</string> <string name="miniresolver_call_information" msgid="6739417525304184083">"आपके संगठन ने, सिर्फ़ वर्क ऐप्लिकेशन से कॉल करने की अनुमति दी है"</string> <string name="miniresolver_sms_information" msgid="4311292661329483088">"आपके संगठन ने, सिर्फ़ वर्क ऐप्लिकेशन से मैसेज भेजने की अनुमति दी है"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"निजी ब्राउज़र का इस्तेमाल करें"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 38d3f01be2ff..4ba22b28bcbc 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -996,7 +996,7 @@ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"Otključavanje SIM-a…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Netočno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> zamolit ćemo vas da otključate tabletno računalo putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> put/a. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> zamolit ćemo vas da otključate Android TV uređaj putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g> morat ćete otključati telefon putem prijave na Google.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> s."</string> @@ -1668,7 +1668,7 @@ <string name="kg_login_invalid_input" msgid="8292367491901220210">"Nevažeće korisničko ime ili zaporka."</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Zaboravili ste korisničko ime ili zaporku?\nPosjetite "<b>"google.com/accounts/recovery"</b>"."</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"Provjera računa..."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Netočno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Netočno ste pokušali otključati tabletno računalo <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. Ono će se vratiti na tvorničke postavke i svi korisnički podaci bit će izgubljeni nakon još ovoliko neuspjelih pokušaja: <xliff:g id="NUMBER_1">%2$d</xliff:g>."</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Toj aplikaciji nije moguće pristupiti na vašem uređaju <xliff:g id="DEVICE">%1$s</xliff:g>. Pokušajte joj pristupiti na telefonu."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ova je aplikacija razvijena za stariju verziju Androida. Možda neće funkcionirati pravilno i ne uključuje najnovije zaštite sigurnosti i privatnosti. Provjerite je li za tu aplikaciju dostupno ažuriranje ili se obratite razvojnom programeru."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Provjeri ažuriranja"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Aplikacija nije kompatibilna s najnovijom verzijom Androida. Provjerite je li za tu aplikaciju dostupno ažuriranje ili se obratite razvojnom programeru."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorite SMS aplikaciju da biste pregledali poruke"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Funkcije mogu biti ograničene"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 0525338b4abd..38fd4ac989a2 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ehhez nem lehet hozzáférni a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>. Próbálja újra a telefonon."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ez az alkalmazás az Android egy korábbi verziójához készült. Előfordulhat, hogy nem működik megfelelően, és nem tartalmazza a legfrissebb biztonsági és adatvédelmi megoldásokat. Keressen frissítést, vagy forduljon az app fejlesztőjéhez."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Frissítés keresése"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ez az alkalmazás nem kompatibilis az Android legújabb verziójával. Keressen frissítést, vagy forduljon az app fejlesztőjéhez."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Új üzenetei érkeztek"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"SMS-alkalmazás megnyitása a megtekintéshez"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Egyes funkciók korlátozva lehetnek"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index db5acb9349dd..272dfee4002f 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -681,8 +681,8 @@ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Թույլ լուսավորություն"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"Փոքր-ինչ հեռու պահեք հեռախոսը"</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"Մոտեցրեք հեռախոսը"</string> - <string name="face_acquired_too_high" msgid="8278815780046368576">"Բարձրացրեք հեռախոսը"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"Իջեցրեք հեռախոսը"</string> + <string name="face_acquired_too_high" msgid="8278815780046368576">"Պահեք հեռախոսն ավելի վերև"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"Պահեք հեռախոսն ավելի ներքև"</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"Հեռախոսը շարժեք դեպի ձախ"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Հեռախոսը շարժեք դեպի աջ"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Նայեք ուղիղ էկրանին։"</string> @@ -1370,7 +1370,7 @@ <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Հայտնաբերված է անալոգային աուդիո լրասարք"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Կցված սարքը համատեղելի չէ այս հեռախոսի հետ: Հպեք` ավելին իմանալու համար:"</string> <string name="adb_active_notification_title" msgid="408390247354560331">"USB-ով վրիպազերծումը միացված է"</string> - <string name="adb_active_notification_message" msgid="5617264033476778211">"Հպեք՝ անջատելու համար"</string> + <string name="adb_active_notification_message" msgid="5617264033476778211">"Հպեք անջատելու համար"</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Սեղմեք՝ USB-ով վրիպազերծումն անջատելու համար:"</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Անլար վրիպազերծումը միացված է"</string> <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Հպեք՝ անլար վրիպազերծումն անջատելու համար"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Այս գործառույթը հասանելի չէ <xliff:g id="DEVICE">%1$s</xliff:g> սարքում։ Օգտագործեք ձեր հեռախոսը։"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Այս հավելվածը մշակված է Android-ի ավելի հին տարբերակի համար։ Այն չի համապատասխանում անվտանգության և գաղտնիության ապահովման վերջին պահանջներին և կարող է պատշաճ կերպով չաշխատել։ Ստուգեք թարմացումների առկայությունը կամ դիմեք հավելվածի մշակողին։"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Ստուգել նոր տարբերակի առկայությունը"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Այս հավելվածը համատեղելի չէ Android-ի վերջին տարբերակի հետ։ Ստուգեք թարմացումների առկայությունը կամ դիմեք հավելվածը մշակողին։"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Դուք ունեք նոր հաղորդագրություններ"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Դիտելու համար բացել SMS-ների փոխանակման հավելվածը"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Որոշ գործառույթներ կարող են չաշխատել"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 4609fdf0301f..c854e12fa4e3 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -475,7 +475,7 @@ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Aplikasi ini dapat menambahkan, menghapus, atau mengubah acara kalender di ponsel. Aplikasi ini dapat mengirim pesan yang kelihatannya berasal dari pemilik kalender, atau mengubah acara tanpa memberi tahu pemilik."</string> <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"akses perintah penyedia lokasi ekstra"</string> <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Memungkinkan aplikasi mengakses perintah penyedia lokasi ekstra. Tindakan ini memungkinkan aplikasi mengganggu pengoperasian GPS atau sumber lokasi lain."</string> - <string name="permlab_accessFineLocation" msgid="6426318438195622966">"akses lokasi akurat hanya saat di latar depan"</string> + <string name="permlab_accessFineLocation" msgid="6426318438195622966">"akses lokasi presisi hanya saat di latar depan"</string> <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Aplikasi ini bisa mendapatkan lokasi pasti Anda dari layanan lokasi saat aplikasi sedang digunakan. Layanan lokasi untuk perangkat harus diaktifkan agar aplikasi bisa mendapatkan lokasi. Ini dapat meningkatkan penggunaan baterai."</string> <string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"akses perkiraan lokasi hanya saat berada di latar depan"</string> <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Aplikasi ini bisa mendapatkan perkiraan lokasi Anda dari layanan lokasi saat aplikasi sedang digunakan. Layanan lokasi untuk perangkat harus diaktifkan agar aplikasi bisa mendapatkan lokasi."</string> @@ -1695,7 +1695,7 @@ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan aktifkan"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"AKTIF"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"NONAKTIF"</string> - <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Izinkan <xliff:g id="SERVICE">%1$s</xliff:g> memiliki kontrol penuh atas perangkat Anda?"</string> + <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Izinkan <xliff:g id="SERVICE">%1$s</xliff:g> mengontrol perangkat Anda secara penuh?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"Kontrol penuh sesuai untuk aplikasi yang mendukung kebutuhan aksesibilitas Anda, tetapi tidak untuk sebagian besar aplikasi."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Melihat dan mengontrol layar"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Voice Access dapat membaca semua konten di layar dan menampilkan konten di atas aplikasi lain."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Setelan ini tidak dapat diakses di <xliff:g id="DEVICE">%1$s</xliff:g>. Coba di ponsel."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Aplikasi ini dibuat untuk versi lama Android. Aplikasi mungkin tidak berfungsi dengan baik dan tidak menyertakan perlindungan privasi dan keamanan terbaru. Periksa update, atau hubungi developer aplikasi."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Periksa apakah ada update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Aplikasi ini tidak kompatibel dengan versi terbaru Android. Periksa update atau hubungi developer aplikasi."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Ada pesan baru"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Buka aplikasi SMS untuk melihat"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Beberapa fitur tidak dapat digunakan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 30f0bd988f70..1c7a07df45a8 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ekki er hægt að opna þetta í <xliff:g id="DEVICE">%1$s</xliff:g>. Prófaðu það í símanum í staðinn."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Þetta forrit var hannað fyrir eldri útgáfu af Android. Óvíst er að það virki rétt og það inniheldur ekki nýjustu öryggis- og persónuverndarvarnirnar. Leitaðu að uppfærslu eða hafðu samband við þróunaraðila forritsins."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Leita að uppfærslu"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Þetta forrit er ekki samhæft nýjustu útgáfu Android. Athugaðu hvort uppfærsla sé í boði eða hafðu samband við þróunaraðila forritsins."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Þú ert með ný skilaboð"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Opnaðu SMS-forritið til að skoða"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Sum virkni kann að vera takmörkuð"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index f5c1c33e563a..2e9d1ec2158c 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -682,10 +682,10 @@ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Luce insufficiente"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"Allontana il telefono"</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"Avvicina il telefono"</string> - <string name="face_acquired_too_high" msgid="8278815780046368576">"Sposta il telefono più in alto"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"Sposta il telefono più in basso"</string> - <string name="face_acquired_too_right" msgid="6245286514593540859">"Sposta il telefono verso sinistra"</string> - <string name="face_acquired_too_left" msgid="9201762240918405486">"Sposta il telefono verso destra"</string> + <string name="face_acquired_too_high" msgid="8278815780046368576">"Sposta lo smartphone più in alto"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"Sposta lo smartphone più in basso"</string> + <string name="face_acquired_too_right" msgid="6245286514593540859">"Sposta lo smartphone verso sinistra"</string> + <string name="face_acquired_too_left" msgid="9201762240918405486">"Sposta lo smartphone verso destra"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Guarda più direttamente verso il dispositivo."</string> <string name="face_acquired_not_detected" msgid="1057966913397548150">"Volto non visible. Tieni lo smartphone all\'altezza degli occhi."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Troppo movimento. Tieni fermo il telefono."</string> @@ -1716,7 +1716,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"Correzione del colore"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modalità a una mano"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Attenuazione extra"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Apparecchi acustici"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Protesi uditive"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Rilascia i tasti del volume. Per attivare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tieni di nuovo premuti entrambi i tasti del volume per 3 secondi."</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Non è possibile accedere su <xliff:g id="DEVICE">%1$s</xliff:g>. Prova a usare il telefono."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Questa app è stata progettata per una versione precedente di Android. Potrebbe non funzionare correttamente e non include le protezioni della sicurezza e della privacy più recenti. Verifica la presenza di un aggiornamento o contatta lo sviluppatore dell\'app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Cerca aggiornamenti"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Questa app non è compatibile con la versione più recente di Android. Verifica la presenza di un aggiornamento o contatta lo sviluppatore dell\'app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Hai nuovi messaggi"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Apri l\'app SMS per la visualizzazione"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Alcune funzionalità sono limitate"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 4b0a87415a7b..84f56ff54f36 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -687,7 +687,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"צריך להזיז את הטלפון שמאלה"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"צריך להזיז את הטלפון ימינה"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"יש להביט ישירות אל המכשיר."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"אי אפשר לראות את הפנים שלך. יש להחזיק את הטלפון בגובה העיניים."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"כדי לראות את הפנים שלך יש להחזיק את הטלפון בגובה העיניים."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"יותר מדי תנועה. יש להחזיק את הטלפון בצורה יציבה."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"יש לסרוק שוב את הפנים."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"לא ניתן לזהות את הפנים. יש לנסות שוב."</string> @@ -1696,8 +1696,8 @@ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"לא להפעיל"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"מופעל"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"כבוי"</string> - <string name="accessibility_enable_service_title" msgid="3931558336268541484">"להעניק לשירות <xliff:g id="SERVICE">%1$s</xliff:g> שליטה מלאה במכשיר?"</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"האפשרות לשליטה מלאה במכשיר לא מתאימה לכל האפליקציות, אלא רק לאפליקציות שעוזרות עם צורכי הנגישות שלך."</string> + <string name="accessibility_enable_service_title" msgid="3931558336268541484">"להעניק לשירות \'<xliff:g id="SERVICE">%1$s</xliff:g>\' שליטה מלאה במכשיר?"</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"האפשרות לשליטה מלאה במכשיר לא מתאימה לרוב האפליקציות, אלא רק לאפליקציות שעוזרות עם צורכי הנגישות שלך."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"הצגת המסך ושליטה בו"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"אפשרות לקריאת כל התוכן במסך ולהצגת התוכן מעל אפליקציות אחרות."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"הצגה וביצוע של פעולות"</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"אי אפשר לגשת להגדרה הזו במכשיר <xliff:g id="DEVICE">%1$s</xliff:g>. במקום זאת, אפשר לנסות בטלפון."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"האפליקציה הזו תוכננה לגרסה ישנה יותר של Android. האפליקציה לא כוללת את אמצעי ההגנה האחרונים לאבטחה ופרטיות, ועלולה לא לעבוד כראוי. כדאי לבדוק אם יש עדכון או ליצור קשר עם מפתח האפליקציה."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"יש עדכון חדש?"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"האפליקציה הזו לא תואמת לגרסה העדכנית של Android. כדאי לבדוק אם יש עדכון או ליצור קשר עם מפתחי האפליקציה."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"יש לך הודעות חדשות"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"יש לפתוח את אפליקציית ה-SMS כדי להציג"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ייתכן שחלק מהפונקציונליות תהיה מוגבלת"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 43925cc0b2af..dc4694d03bed 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g> からはアクセスできません。スマートフォンでのアクセスをお試しください。"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"このアプリは Android の以前のバージョンを対象としているため、正しく動作しない可能性があります。最新のセキュリティ保護やプライバシー保護は組み込まれていません。アップデートをご確認いただくか、アプリのデベロッパーにお問い合わせください。"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"アップデートを確認"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"このアプリは最新バージョンの Android に対応していません。アップデートをご確認いただくか、アプリのデベロッパーにお問い合わせください。"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"新着メッセージがあります"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"表示するには SMS アプリを開きます"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"一部の機能が制限されることがあります"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index f22942d7bc20..13d430c985fe 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ამჟამად ამ აპზე თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან წვდომა შეუძლებელია. ცადეთ ტელეფონიდან."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ეს აპი Android-ის ძველი ვერსიისთვის შეიქმნა. ის შესაძლოა არ მიშაობდეს სწორად და არ შეიცავდეს უსაფრთხოებისა და კონფიდენციალურობის უახლეს დაცვას. შეამოწმეთ განახლება, ან დაუკავშირდით აპის დეველოპერს."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"განახლების შემოწმება"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ეს აპი არ არის თავსებადი Android-ის უახლეს ვერსიასთან. შეამოწმეთ განახლება, ან დაუკავშირდით აპის დეველოპერს."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"თქვენ ახალი შეტყობინებები გაქვთ"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"სანახავად, გახსენით SMS აპი"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ზოგიერთი ფუნქცია შეიძლება შეიზღუდოს"</string> @@ -2171,7 +2172,7 @@ <string name="miniresolver_sms_information" msgid="4311292661329483088">"თქვენი ორგანიზაცია ნებას გრთავთ, მხოლოდ სამსახურის აპებიდან გაგზავნოთ შეტყობინებები"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"პირადი ბრაუზერის გამოყენება"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"სამსახურის ბრაუზერის გამოყენება"</string> - <string name="miniresolver_call" msgid="6386870060423480765">"ზარი"</string> + <string name="miniresolver_call" msgid="6386870060423480765">"დარეკვა"</string> <string name="miniresolver_switch" msgid="8011924662117617451">"გადართვა"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIM ქსელის განბლოკვის PIN-კოდი"</string> <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"SIM ქსელის ქვედანაყოფის განბლოკვის PIN-კოდი"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index e972e8c8ccd9..03897c1e90f7 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Бұған <xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан кіру мүмкін емес. Оның орнына телефонды пайдаланып көріңіз."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Бұл қолданба Android жүйесінің ескі нұсқасына арналған. Ол дұрыс жұмыс істемеуі және онда соңғы қауіпсіздік пен құпиялықты сақтау құралдары болмауы мүмкін. Қолданбаның жаңа нұсқасы бар-жоғын тексеріңіз не оны әзірлеушіге хабарласыңыз."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Жаңарту бар-жоғын тексеру"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Бұл қолданба Android-тың соңғы нұсқасымен үйлесімді емес. Қолданбаның жаңа нұсқасы бар-жоғын тексеріңіз не әзірлеушіге хабарласыңыз."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Сізде жаңа хабарлар бар"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Көру үшін SMS қолданбасын ашыңыз"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Кейбір функциялар істемеуі мүмкін."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 9dc2d97be321..d7f979f49ee5 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"ការកែតម្រូវពណ៌"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"មុខងារប្រើដៃម្ខាង"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ពន្លឺតិចខ្លាំង"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ឧបករណ៍ស្តាប់"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់គ្រាប់ចុចកម្រិតសំឡេងជាប់។ បានបិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"លែងគ្រាប់ចុចកម្រិតសំឡេង។ ដើម្បីបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g> សូមចុចគ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់ម្ដងទៀតរយៈពេល 3 វិនាទី។"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"មិនអាចចូលប្រើប្រាស់កម្មវិធីនេះនៅលើ <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ។ សូមសាកល្បងប្រើនៅលើទូរសព្ទរបស់អ្នកជំនួសវិញ។"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"កម្មវិធីនេះត្រូវបានបង្កើតឡើងសម្រាប់កំណែ Android ចាស់ជាងនេះ។ កម្មវិធីនេះអាចមិនដំណើរការបានត្រឹមត្រូវ និងមិនរួមបញ្ចូលការការពារឯកជនភាព និងសុវត្ថិភាពចុងក្រោយបំផុតទេ។ ពិនិត្យរកមើលកំណែថ្មី ឬទាក់ទងទៅអ្នកអភិវឌ្ឍន៍កម្មវិធី។"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"រកមើលកំណែថ្មី"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"កម្មវិធីនេះមិនត្រូវគ្នាជាមួយកំណែ Android ចុងក្រោយបំផុតទេ។ សូមពិនិត្យរកមើលកំណែថ្មី ឬទាក់ទងទៅអ្នកអភិវឌ្ឍន៍កម្មវិធី។"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"អ្នកមានសារថ្មី"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"បើកកម្មវិធីសារ SMS ដើម្បីមើល"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"មុខងារមួយចំនួនអាចត្រូវបានកម្រិត"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index c6d6affac1f4..55a0a8430122 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -66,7 +66,7 @@ <string name="ThreeWCMmi" msgid="2436550866139999411">"ಮೂರು ಮಾರ್ಗದಲ್ಲಿ ಕರೆ ಮಾಡುವಿಕೆ"</string> <string name="RuacMmi" msgid="1876047385848991110">"ಅನಪೇಕ್ಷಿತ ಕಿರಿಕಿರಿ ಮಾಡುವ ಕರೆಗಳ ತಿರಸ್ಕಾರ"</string> <string name="CndMmi" msgid="185136449405618437">"ಕರೆ ಮಾಡುವ ಸಂಖ್ಯೆಯ ಡೆಲಿವರಿ"</string> - <string name="DndMmi" msgid="8797375819689129800">"ಅಡಚಣೆ ಮಾಡಬೇಡ"</string> + <string name="DndMmi" msgid="8797375819689129800">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string> <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"ಕರೆಮಾಡುವವರ ID ಅನ್ನು ನಿರ್ಬಂಧಿಸುವಂತೆ ಡಿಫಾಲ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದಿನ ಕರೆ: ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string> <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ಕರೆಮಾಡುವವರ ID ಅನ್ನು ನಿರ್ಬಂಧಿಸುವಂತೆ ಡಿಫಾಲ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದಿನ ಕರೆ: ನಿರ್ಬಂಧಿಸಿಲ್ಲ"</string> <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ಕರೆಮಾಡುವವರ ID ಅನ್ನು ನಿರ್ಬಂಧಿಸದಿರುವಂತೆ ಡಿಫಾಲ್ಟ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದಿನ ಕರೆ: ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string> @@ -638,8 +638,8 @@ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ಪ್ರತಿ ಬಾರಿಯೂ ನಿಮ್ಮ ಬೆರಳಿನ ಸ್ಥಾನವನ್ನು ಸ್ವಲ್ಪ ಮಟ್ಟಿಗೆ ಬದಲಾಯಿಸಿ"</string> <string-array name="fingerprint_acquired_vendor"> </string-array> - <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> - <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> + <string name="fingerprint_error_not_match" msgid="4599441812893438961">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> + <string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string> <string name="fingerprint_authenticated" msgid="2024862866860283100">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string> <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string> @@ -1695,10 +1695,10 @@ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"ಆನ್ ಮಾಡಬೇಡಿ"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ಆನ್ ಆಗಿದೆ"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ಆಫ್ ಆಗಿದೆ"</string> - <string name="accessibility_enable_service_title" msgid="3931558336268541484">"ನಿಮ್ಮ ಸಾಧನದ ಸಂಪೂರ್ಣ ನಿಯಂತ್ರಣವನ್ನು ಹೊಂದಲು <xliff:g id="SERVICE">%1$s</xliff:g>ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string> + <string name="accessibility_enable_service_title" msgid="3931558336268541484">"ನಿಮ್ಮ ಸಾಧನದ ಸಂಪೂರ್ಣ ನಿಯಂತ್ರಣವನ್ನು ಹೊಂದಲು <xliff:g id="SERVICE">%1$s</xliff:g> ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಅವಶ್ಯಕತೆಗಳ ಕುರಿತು ನಿಮಗೆ ಸಹಾಯ ಮಾಡುವ ಆ್ಯಪ್ಗಳಿಗೆ ಸಂಪೂರ್ಣ ನಿಯಂತ್ರಣ ನೀಡುವುದು ಸೂಕ್ತವಾಗಿರುತ್ತದೆ, ಆದರೆ ಬಹುತೇಕ ಆ್ಯಪ್ಗಳಿಗೆ ಇದು ಸೂಕ್ತವಲ್ಲ."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"ಸ್ಕ್ರೀನ್ ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿಯಂತ್ರಿಸಿ"</string> - <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ಇದು ಪರದೆಯ ಮೇಲಿನ ಎಲ್ಲಾ ವಿಷಯವನ್ನು ಓದಬಹುದು ಮತ್ತು ಇತರ ಆ್ಯಪ್ಗಳ ಮೇಲೆ ವಿಷಯವನ್ನು ಪ್ರದರ್ಶಿಸಬಹುದು."</string> + <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ಇದು ಪರದೆಯ ಮೇಲಿನ ಎಲ್ಲಾ ಕಂಟೆಂಟ್ ಅನ್ನು ಓದಬಹುದು ಮತ್ತು ಇತರ ಆ್ಯಪ್ಗಳ ಮೇಲೆ ಕಂಟೆಂಟ್ ಅನ್ನು ಪ್ರದರ್ಶಿಸಬಹುದು."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"ಕ್ರಿಯೆಗಳನ್ನು ವೀಕ್ಷಿಸಿ ಮತ್ತು ನಿರ್ವಹಿಸಿ"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"ಇದು ಆ್ಯಪ್ ಅಥವಾ ಹಾರ್ಡ್ವೇರ್ ಸೆನ್ಸರ್ನ ಜೊತೆಗಿನ ನಿಮ್ಮ ಸಂವಹನಗಳನ್ನು ಟ್ರ್ಯಾಕ್ ಮಾಡಬಹುದು, ಮತ್ತು ನಿಮ್ಮ ಪರವಾಗಿ ಆ್ಯಪ್ಗಳ ಜೊತೆ ಸಂವಹನ ನಡೆಸಬಹುದು."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"ಅನುಮತಿಸಿ"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ನಲ್ಲಿ ಇದನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅದರ ಬದಲು ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ಈ ಆ್ಯಪ್ ಅನ್ನು Android ನ ಹಳೆಯ ಆವೃತ್ತಿಗಾಗಿ ರಚಿಸಲಾಗಿದೆ. ಇದು ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು ಮತ್ತು ಇತ್ತೀಚಿನ ಭದ್ರತೆ ಮತ್ತು ಗೌಪ್ಯತಾ ರಕ್ಷಣೆಗಳನ್ನು ಒಳಗೊಂಡಿರುವುದಿಲ್ಲ. ಅಪ್ಡೇಟ್ಗಾಗಿ ಪರಿಶೀಲಿಸಿ ಅಥವಾ ಆ್ಯಪ್ ಡೆವಲಪರ್ ಅವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ಅಪ್ಡೇಟ್ಗಾಗಿ ಪರಿಶೀಲಿಸಿ"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ಈ ಆ್ಯಪ್ Android ನ ಇತ್ತೀಚಿನ ಆವೃತ್ತಿಯ ಜೊತೆಗೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ. ಅಪ್ಡೇಟ್ಗಾಗಿ ಪರಿಶೀಲಿಸಿ ಅಥವಾ ಆ್ಯಪ್ ಡೆವಲಪರ್ ಅನ್ನು ಸಂಪರ್ಕಿಸಿ."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"ನೀವು ಹೊಸ ಸಂದೇಶಗಳನ್ನು ಹೊಂದಿರುವಿರಿ"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"ವೀಕ್ಷಿಸಲು SMS ಅಪ್ಲಿಕೇಶನ್ ತೆರೆಯಿರಿ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ಕೆಲವು ಕಾರ್ಯನಿರ್ವಹಣೆ ಸೀಮಿತವಾಗಿರಬಹುದು"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index a4d4a7338060..93a7352eb7e0 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"색상 보정"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"한 손 모드"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"보청기"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"청각 보조 기기"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"볼륨 키에서 손을 뗍니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>을 켜려면 볼륨 키 2개를 3초 동안 길게 누르세요."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"<xliff:g id="DEVICE">%1$s</xliff:g>에서는 액세스할 수 없습니다. 대신 휴대전화에서 시도해 보세요."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"이 앱은 Android 이전 버전에 맞게 설계되었습니다. 정상 동작하지 않을 수 있으며 최신 보안 및 개인 정보 보호 기능을 포함하지 않습니다. 업데이트를 확인하거나 앱 개발자에게 문의하세요."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"업데이트 확인"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"이 앱은 Android 최신 버전과 호환되지 않습니다. 업데이트를 확인하거나 앱 개발자에게 문의하세요."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"새 메시지 있음"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"SMS 앱을 열고 확인"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"일부 기능이 제한될 수 있음"</string> @@ -2167,8 +2168,8 @@ <string name="miniresolver_open_in_work" msgid="941341494673509916">"직장 <xliff:g id="APP">%s</xliff:g> 앱에서 여시겠습니까?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"직장 앱을 사용한 통화인가요?"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"직장 앱으로 전환할까요?"</string> - <string name="miniresolver_call_information" msgid="6739417525304184083">"귀하의 조직에서 직장 앱을 사용한 통화만 허용했습니다."</string> - <string name="miniresolver_sms_information" msgid="4311292661329483088">"귀하의 조직에서 직장 앱을 사용한 메시지 전송만 허용했습니다."</string> + <string name="miniresolver_call_information" msgid="6739417525304184083">"귀하의 조직에서 직장 앱을 사용한 통화만 허용합니다."</string> + <string name="miniresolver_sms_information" msgid="4311292661329483088">"귀하의 조직에서 직장 앱을 사용한 메시지 전송만 허용합니다."</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"개인 브라우저 사용"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"직장 브라우저 사용"</string> <string name="miniresolver_call" msgid="6386870060423480765">"통화"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index f599c2cea0f7..5cd22384f59c 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -41,7 +41,7 @@ <string name="badPin" msgid="888372071306274355">"Терилген эски PIN код туура эмес."</string> <string name="badPuk" msgid="4232069163733147376">"Терилген PUK код туура эмес."</string> <string name="mismatchPin" msgid="2929611853228707473">"Терилген PIN\'дер дал келбейт."</string> - <string name="invalidPin" msgid="7542498253319440408">"Узундугу 4төн 8ге чейинки сандан турган PIN-кодду териңиз."</string> + <string name="invalidPin" msgid="7542498253319440408">"Узундугу 4төн 8ге чейинки сандан турган PIN кодду териңиз."</string> <string name="invalidPuk" msgid="8831151490931907083">"Узундугу 8 же көбүрөөк сандан турган PUK-кодду териңиз."</string> <string name="needPuk" msgid="3503414069503752211">"SIM картаңыз PUK менен кулпуланган. Кулпусун ачуу үчүн PUK кодун териңиз."</string> <string name="needPuk2" msgid="3910763547447344963">"SIM картаны бөгөттөн чыгаруу үчүн PUK2 кодун териңиз."</string> @@ -957,7 +957,7 @@ <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Сырсөздү терүү үчүн таптаңыз"</font></string> <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Кулпуну ачуу үчүн сырсөздү териңиз"</string> <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Кулпуну ачуу үчүн PIN кодду териңиз"</string> - <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN-код туура эмес."</string> + <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN код туура эмес."</string> <string name="keyguard_label_text" msgid="3841953694564168384">"Кулпусун ачуу үчүн Менюну андан соң 0 баскычын басыңыз."</string> <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Өзгөчө кырдаалдар кызматы"</string> <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Интернет жок"</string> @@ -995,7 +995,7 @@ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"SIM картанын кулпусу ачылууда…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string> <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Сырсөзүңүздү <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"PIN-кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"PIN кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, планшетиңиздин кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Графикалык ачкычыңызды <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес чийдиңиз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, Android TV түзмөгүңүздүн кулпусун Google аккаунтуңузга кирип ачышыңыз керек болот.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секунддан кийин кайталап көрүңүз."</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> жолу туура эмес тартсаңыз, телефонуңуздун кулпусун Google\'га кирип ачууга туура келет.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> секундадан кийин дагы аракет кылып көрүңүз."</string> @@ -1043,7 +1043,7 @@ <string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Үлгү менен ачуу."</string> <string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Жүзүнөн таанып ачуу."</string> <string name="keyguard_accessibility_pin_unlock" msgid="4020864007967340068">"Пин код менен ачуу."</string> - <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM-картанын кулпусун PIN-код менен ачуу."</string> + <string name="keyguard_accessibility_sim_pin_unlock" msgid="4895939120871890557">"SIM-картанын кулпусун PIN код менен ачуу."</string> <string name="keyguard_accessibility_sim_puk_unlock" msgid="3459003464041899101">"SIM-картанын кулпусун PUK-код менен ачуу."</string> <string name="keyguard_accessibility_password_unlock" msgid="6130186108581153265">"Сырсөз менен ачуу."</string> <string name="keyguard_accessibility_pattern_area" msgid="1419570880512350689">"Үлгү аймагы."</string> @@ -1409,7 +1409,7 @@ <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"Медиа сактагычты талдоо"</string> <string name="ext_media_new_notification_title" msgid="3517407571407687677">"Жаңы <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> иштебей жатат"</string> - <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Жөндөө үчүн таптаңыз"</string> + <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Орнотуу үчүн басыңыз"</string> <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Жөндөө үчүн тандаңыз"</string> <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Түзмөктү форматташыңыз керек болушу мүмкүн. Чыгаруу үчүн таптап коюңуз."</string> <string name="ext_media_ready_notification_message" msgid="7509496364380197369">"Сүрөттөрдү, видеолорду, ырларды жана башкаларды сактоо үчүн керек"</string> @@ -1645,17 +1645,17 @@ <string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"Үлгү унутулду"</string> <string name="kg_wrong_pattern" msgid="1342812634464179931">"Графикалык ачкыч туура эмес"</string> <string name="kg_wrong_password" msgid="2384677900494439426">"Сырсөз туура эмес"</string> - <string name="kg_wrong_pin" msgid="3680925703673166482">"PIN-код туура эмес"</string> + <string name="kg_wrong_pin" msgid="3680925703673166482">"PIN код туура эмес"</string> <string name="kg_pattern_instructions" msgid="8366024510502517748">"Бөгөттөн чыгаруу сүрөтүн тартыңыз"</string> <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"SIM-картанын PIN\'ин киргизиңиз"</string> <string name="kg_pin_instructions" msgid="7355933174673539021">"PIN\'ди киргизиңиз"</string> <string name="kg_password_instructions" msgid="7179782578809398050">"Сырсөз киргизиңиз"</string> <string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIM-карта азыр жарактан чыкты. Улантыш үчүн, PUK-кодду киргизиңиз. Көбүрөөк маалымат үчүн операторуңузга кайрылыңыз."</string> - <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Сиз каалаган PIN-кодду териңиз"</string> - <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Сиз каалаган PIN-кодду ырастаңыз"</string> + <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Сиз каалаган PIN кодду териңиз"</string> + <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Сиз каалаган PIN кодду ырастаңыз"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="5743634657721110967">"SIM картанын кулпусу ачылууда…"</string> - <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN-код туура эмес."</string> - <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Узундугу 4төн 8ге чейинки сандан турган PIN-кодду териңиз."</string> + <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN код туура эмес."</string> + <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Узундугу 4төн 8ге чейинки сандан турган PIN кодду териңиз."</string> <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK-код 8 сандан турушу керек."</string> <string name="kg_invalid_puk" msgid="4809502818518963344">"Туура PUK-кодду кайрадан териңиз. Кайталанган аракеттер SIM картаны биротоло жараксыз кылат."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN-коддор туура келбеди"</string> @@ -1667,7 +1667,7 @@ <string name="kg_login_invalid_input" msgid="8292367491901220210">"Колдонуучу атыңыз же сырсөзүңүз туура эмес."</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Колдонуучу атыңызды же сырсөзүңүздү унутуп калдыңызбы?\n"<b>"google.com/accounts/recovery"</b>" дарегине кайрылыңыз."</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"Эсеп текшерилүүдө…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Сиз PIN-кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Сиз PIN кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Сиз сырсөзүңүздү <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Сиз бөгөттөн чыгаруу үлгүсүн <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес көрсөттүңүз. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> секундадан кийин кайталаңыз."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Сиз планшетиңизди <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу ийгиликсиз бөгөттөн чыгаруу аракетин кылдыңыз. Дагы <xliff:g id="NUMBER_1">%2$d</xliff:g> ийгиликсиз аракеттен кийин, планшет баштапкы абалына кайтарылат жана бардык берилиштериңиз өчүрүлөт."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Буга <xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн кире албайсыз. Анын ордуна телефондон кирип көрүңүз."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Бул колдонмо эски Android версиясы үчүн түзүлгөн. Ал туура иштебеши мүмкүн жана анда коопсуздукту жана купуялыкты коргоонун эң акыркы мүмкүнчүлүктөрү камтылган эмес. Жаңыртууну издеп көрүңүз же колдонмону иштеп чыгуучуга кайрылыңыз."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Жаңыртууларды текшерүү"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Бул колдонмо Android\'дин соңку версиясына шайкеш келбейт. Жаңыртууну издеп көрүңүз же колдонмону иштеп чыгуучуга кайрылыңыз."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Сизге жаңы билдирүүлөр келди"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Көрүү үчүн SMS колдонмосун ачыңыз"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Айрым функциялар иштебеши мүмкүн"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 767720661335..269fa169bd43 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ບໍ່ສາມາດເຂົ້າເຖິງແອັບນີ້ໄດ້ຢູ່ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານ. ກະລຸນາລອງຢູ່ໂທລະສັບຂອງທ່ານແທນ."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ແອັບນີ້ສ້າງຂຶ້ນສຳລັບ Android ເວີຊັນເກົ່າ. ມັນອາດເຮັດວຽກບໍ່ຖືກຕ້ອງ ແລະ ຮວມທັງບໍ່ມີຄວາມປອດໄພ ແລະ ການປ້ອງກັນຄວາມເປັນສ່ວນຕົວຫຼ້າສຸດ. ກວດສອບເພື່ອອັບເດດ ຫຼື ຕິດຕໍ່ນັກພັດທະນາແອັບ."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ກວດເບິ່ງອັບເດດ"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ແອັບນີ້ບໍ່ສາມາດໃຊ້ໄດ້ກັບ Android ເວີຊັນຫຼ້າສຸດ. ກວດສອບເພື່ອອັບເດດ ຫຼື ຕິດຕໍ່ນັກພັດທະນາແອັບ."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"ທ່ານມີຂໍ້ຄວາມໃໝ່"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"ເປີດແອັບ SMS ເພື່ອເບິ່ງ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ຄຸນສົມບັດບາງຢ່າງອາດຖືກຈຳກັດໄວ້"</string> @@ -2167,8 +2168,8 @@ <string name="miniresolver_open_in_work" msgid="941341494673509916">"ເປີດໃນ <xliff:g id="APP">%s</xliff:g> ສຳລັບວຽກບໍ?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"ໂທຈາກແອັບບ່ອນເຮັດວຽກບໍ?"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"ສະຫຼັບໄປເປັນແອັບບ່ອນເຮັດວຽກບໍ?"</string> - <string name="miniresolver_call_information" msgid="6739417525304184083">"ອົງການຈັດຕັ້ງຂອງທ່ານອະນຸຍາດໃຫ້ທ່ານໂທຈາກແອັບບ່ອນເຮັດວຽກເທົ່ານັ້ນ"</string> - <string name="miniresolver_sms_information" msgid="4311292661329483088">"ອົງການຈັດຕັ້ງຂອງທ່ານອະນຸຍາດໃຫ້ທ່ານສົ່ງຂໍ້ຄວາມໄດ້ຈາກແອັບບ່ອນເຮັດວຽກເທົ່ານັ້ນ"</string> + <string name="miniresolver_call_information" msgid="6739417525304184083">"ອົງການຂອງທ່ານອະນຸຍາດໃຫ້ທ່ານໂທຈາກແອັບບ່ອນເຮັດວຽກເທົ່ານັ້ນ"</string> + <string name="miniresolver_sms_information" msgid="4311292661329483088">"ອົງການຂອງທ່ານອະນຸຍາດໃຫ້ທ່ານສົ່ງຂໍ້ຄວາມໄດ້ຈາກແອັບບ່ອນເຮັດວຽກເທົ່ານັ້ນ"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"ໃຊ້ໂປຣແກຣມທ່ອງເວັບສ່ວນຕົວ"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"ໃຊ້ໂປຣແກຣມທ່ອງເວັບບ່ອນເຮັດວຽກ"</string> <string name="miniresolver_call" msgid="6386870060423480765">"ໂທ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index eb1ab564c283..4ae49ac1207d 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nepavyksta pasiekti nuotolinio įrenginio iš jūsų „<xliff:g id="DEVICE">%1$s</xliff:g>“. Pabandykite naudoti telefoną."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ši programa sukurta senesnės versijos „Android“. Ji gali tinkamai neveikti ir joje nėra naujausių saugos ir privatumo apsaugos priemonių. Patikrinkite, ar yra naujinių, arba susisiekite su programos kūrėju."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tikrinti, ar yra naujinių"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ši programa nesuderinama su naujausios versijos „Android“. Patikrinkite, ar yra naujinių, arba susisiekite su programos kūrėju."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Turite naujų pranešimų"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Atidaryti SMS programą, norint peržiūrėti"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Kai kurios funkcijos ribojamos"</string> @@ -2337,7 +2338,7 @@ <string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"Klaviatūros išdėstymas nustatytas į <xliff:g id="LAYOUT_1">%s</xliff:g>. Palieskite, kad pakeistumėte."</string> <string name="keyboard_layout_notification_two_selected_message" msgid="1876349944065922950">"Klaviatūros išdėstymas nustatytas į <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>. Palieskite, kad pakeistumėte."</string> <string name="keyboard_layout_notification_three_selected_message" msgid="280734264593115419">"Klaviatūros išdėstymas nustatytas į <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>. Palieskite, kad pakeistumėte."</string> - <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klaviatūros išdėstymas nustatytas į <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Palieskite, kad pakeistumėte."</string> + <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Išdėstymas nustatytas į <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Palieskite, kad pakeistumėte."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Sukonfigūruotos fizinės klaviatūros"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Palieskite, kad peržiūrėtumėte klaviatūras"</string> </resources> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 249fb1f29464..0c0082967dff 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1698,7 +1698,7 @@ <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"IZSLĒGTA"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Vai atļaut pakalpojumam <xliff:g id="SERVICE">%1$s</xliff:g> pilnībā kontrolēt jūsu ierīci?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"Pilnīga kontrole ir piemērota lietotnēm, kas nepieciešamas lietotājiem ar īpašām vajadzībām, taču ne lielākajai daļai lietotņu."</string> - <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Skatīt un pārvaldīt ekrānu"</string> + <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ekrāna skatīšana un pārvaldīšana"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Tā var nolasīt visu ekrānā esošo saturu un attēlot saturu citām lietotnēm."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Darbību skatīšana un veikšana"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Tā var izsekot jūsu mijiedarbību ar lietotni vai aparatūras sensoru un mijiedarboties ar lietotnēm jūsu vārdā."</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ierīcē <xliff:g id="DEVICE">%1$s</xliff:g> nevar piekļūt šai funkcijai. Mēģiniet tai piekļūt tālrunī."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Šī lietotne ir paredzēta vecākai Android versijai. Tā var nedarboties pareizi un neietver jaunākās drošības un konfidencialitātes aizsardzības funkcijas. Pārbaudiet atjauninājumu pieejamību vai sazinieties ar lietotnes izstrādātāju."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Meklēt atjauninājumu"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Šī lietotne nav saderīga ar jaunāko Android versiju. Pārbaudiet atjauninājumu pieejamību vai sazinieties ar lietotnes izstrādātāju."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Jums ir jaunas īsziņas."</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Lai skatītu, atveriet īsziņu lietotni."</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Funkcijas var būt ierobežotas"</string> diff --git a/core/res/res/values-mcc310-mnc030-eu/strings.xml b/core/res/res/values-mcc310-mnc030-eu/strings.xml index 45ce09126146..3bd918d70b96 100644 --- a/core/res/res/values-mcc310-mnc030-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc030-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="656054059094417927">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="1782569305985001089">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="1782569305985001089">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="8246632898824321280">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mcc310-mnc170-eu/strings.xml b/core/res/res/values-mcc310-mnc170-eu/strings.xml index 76e30b01c29d..e9f761fb6aac 100644 --- a/core/res/res/values-mcc310-mnc170-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc170-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="5424518490295341205">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="3527626511418944853">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="3527626511418944853">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="3948912590657398489">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mcc310-mnc280-eu/strings.xml b/core/res/res/values-mcc310-mnc280-eu/strings.xml index fbf70441fe4c..13989514e49e 100644 --- a/core/res/res/values-mcc310-mnc280-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc280-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="1070849538022865416">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="499832197298480670">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="499832197298480670">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="2346111479504469688">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mcc310-mnc380-eu/strings.xml b/core/res/res/values-mcc310-mnc380-eu/strings.xml index c3fb1bc649d4..8d7e3553b385 100644 --- a/core/res/res/values-mcc310-mnc380-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc380-eu/strings.xml @@ -21,5 +21,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="6178029798083341927">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="6084322234976891423">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="6084322234976891423">"Ez da onartzen SIMa MM#3"</string> </resources> diff --git a/core/res/res/values-mcc310-mnc410-eu/strings.xml b/core/res/res/values-mcc310-mnc410-eu/strings.xml index b023bccae8b4..dc3a4081a95e 100644 --- a/core/res/res/values-mcc310-mnc410-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc410-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="8861901652350883183">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="2604694337529846283">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="2604694337529846283">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="3099618295079374317">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mcc310-mnc560-eu/strings.xml b/core/res/res/values-mcc310-mnc560-eu/strings.xml index a0a46f666782..8b2e7d41a587 100644 --- a/core/res/res/values-mcc310-mnc560-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc560-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="3526528316378889524">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="4618730283812066268">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="4618730283812066268">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="8522039751358990401">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mcc310-mnc950-eu/strings.xml b/core/res/res/values-mcc310-mnc950-eu/strings.xml index 5a34371f80c7..92de2626a3af 100644 --- a/core/res/res/values-mcc310-mnc950-eu/strings.xml +++ b/core/res/res/values-mcc310-mnc950-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="615419724607901560">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="7801541624846497489">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="7801541624846497489">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="7066936962628406316">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mcc311-mnc180-eu/strings.xml b/core/res/res/values-mcc311-mnc180-eu/strings.xml index d843c4fa4f19..3cacf6bfe086 100644 --- a/core/res/res/values-mcc311-mnc180-eu/strings.xml +++ b/core/res/res/values-mcc311-mnc180-eu/strings.xml @@ -21,6 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr" msgid="604133804161351810">"Ez dago SIMik MM#2"</string> - <string name="mmcc_illegal_ms" msgid="4073997279280371621">"Ez da onartzen SIM txartela MM#3"</string> + <string name="mmcc_illegal_ms" msgid="4073997279280371621">"Ez da onartzen SIMa MM#3"</string> <string name="mmcc_illegal_me" msgid="4936539345546223576">"Telefonoa ez da onartzen MM#6"</string> </resources> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 6c570c230c56..0a4c9bf30243 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -91,7 +91,7 @@ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Статус на мобилна мрежа"</string> <string name="notification_channel_sms" msgid="1243384981025535724">"SMS-пораки"</string> <string name="notification_channel_voice_mail" msgid="8457433203106654172">"Пораки од говорна пошта"</string> - <string name="notification_channel_wfc" msgid="9048240466765169038">"Повикување преку Wi-Fi"</string> + <string name="notification_channel_wfc" msgid="9048240466765169038">"Повици преку Wi-Fi"</string> <string name="notification_channel_sim" msgid="5098802350325677490">"Статус на SIM-картичка"</string> <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"Статус на SIM-известувања со висок приоритет"</string> <string name="peerTtyModeFull" msgid="337553730440832160">"Рамноправен уред го побара режимот на TTY „FULL“"</string> @@ -122,7 +122,7 @@ <string name="roamingTextSearching" msgid="5323235489657753486">"Пребарување за услуга"</string> <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Не може да се постави функцијата „Повици преку Wi-Fi“"</string> <string-array name="wfcOperatorErrorAlertMessages"> - <item msgid="468830943567116703">"За да воспоставувате повици и да испраќате пораки преку Wi-Fi, прво побарајте од операторот да ја постави услугава. Потоа, вклучете ја повторно „Повикување преку Wi-Fi“ во „Поставки“. (Код за грешка: <xliff:g id="CODE">%1$s</xliff:g>)"</item> + <item msgid="468830943567116703">"За да воспоставувате повици и да испраќате пораки преку Wi-Fi, прво побарајте од операторот да ја постави услугава. Потоа, вклучете ја повторно „Повици преку Wi-Fi“ во „Поставки“. (Код за грешка: <xliff:g id="CODE">%1$s</xliff:g>)"</item> </string-array> <string-array name="wfcOperatorErrorNotificationMessages"> <item msgid="4795145070505729156">"Проблем при регистрирањето на функцијата „Повици преку Wi‑Fi“ со операторот: <xliff:g id="CODE">%1$s</xliff:g>"</item> @@ -130,15 +130,15 @@ <!-- no translation found for wfcSpnFormat_spn (2982505428519096311) --> <skip /> <string name="wfcSpnFormat_spn_wifi_calling" msgid="3165949348000906194">"Повици преку Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string> - <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"Повикување преку Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string> + <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen" msgid="3836827895369365298">"Повици преку Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_wlan_call" msgid="4895315549916165700">"Повик преку WLAN"</string> <string name="wfcSpnFormat_spn_wlan_call" msgid="255919245825481510">"Повик преку WLAN на <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_spn_wifi" msgid="7232899594327126970">"Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_wifi_calling_bar_spn" msgid="8383917598312067365">"Повици преку Wi-Fi | <xliff:g id="SPN">%s</xliff:g>"</string> <string name="wfcSpnFormat_spn_vowifi" msgid="6865214948822061486">"Глас преку Wi-Fi на <xliff:g id="SPN">%s</xliff:g>"</string> - <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Повикување преку Wi-Fi"</string> + <string name="wfcSpnFormat_wifi_calling" msgid="6178935388378661755">"Повици преку Wi-Fi"</string> <string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string> - <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Повикување преку Wi-Fi"</string> + <string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Повици преку Wi-Fi"</string> <string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"Глас преку Wi-Fi"</string> <string name="wfcSpnFormat_wifi_call" msgid="434016592539090004">"Повик преку WiFi"</string> <string name="wifi_calling_off_summary" msgid="5626710010766902560">"Исклучено"</string> @@ -1723,7 +1723,7 @@ <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Изберете ја функцијата што ќе ја користите со движењето за пристапност (повлекување нагоре од дното на екранот со два прста):"</string> <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Изберете ја функцијата што ќе ја користите со движењето за пристапност (повлекување нагоре од дното на екранот со три прста):"</string> <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"За префрлање помеѓу функциите, допрете и задржете го копчето за пристапност."</string> - <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"За префрлање помеѓу функциите, повлечете нагоре со два прста и задржете."</string> + <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"За да се префрлите на друга функција, повлечете нагоре со два прста и задржете."</string> <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"За префрлање помеѓу функциите, повлечете нагоре со три прста и задржете."</string> <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Зголемување"</string> <string name="user_switched" msgid="7249833311585228097">"Тековен корисник <xliff:g id="NAME">%1$s</xliff:g>."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Ова не може да се отвори на <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на вашиот телефон како алтернатива."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Оваа апликација е создадена за постара верзија на Android. Можеби нема да работи правилно и не ги вклучува најновите мерки за заштита на безбедноста и приватноста. Проверете дали има ажурирање или контактирајте со програмерот на апликацијата."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверка за ажурирање"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Апликацијава не е компатибилна со најновата верзија на Android. Проверете дали има ажурирање или контактирајте со програмерот на апликацијата."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нови пораки"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Отворете ја апликацијата за SMS за приказ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Некои функции се ограничени"</string> @@ -2167,8 +2168,8 @@ <string name="miniresolver_open_in_work" msgid="941341494673509916">"Да се отвори во работната апликација <xliff:g id="APP">%s</xliff:g>?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"Да се повика од работна апликација?"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"Да се префрли на работна апликација?"</string> - <string name="miniresolver_call_information" msgid="6739417525304184083">"Вашата организацијата ви дозволува да упатувате повици само од работни апликации"</string> - <string name="miniresolver_sms_information" msgid="4311292661329483088">"Вашата организацијата ви дозволува да испраќате пораки само од работни апликации"</string> + <string name="miniresolver_call_information" msgid="6739417525304184083">"Вашата организација ви дозволува да упатувате повици само од работни апликации"</string> + <string name="miniresolver_sms_information" msgid="4311292661329483088">"Вашата организација ви дозволува да испраќате пораки само од работни апликации"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Користи личен прелистувач"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Користи работен прелистувач"</string> <string name="miniresolver_call" msgid="6386870060423480765">"Повикај"</string> @@ -2329,7 +2330,7 @@ <string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"Двојниот екран е недостапен бидејќи вашиот телефон станува претопол"</string> <string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"„Двојниот екран“ е недостапен"</string> <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"Двојниот екран е недостапен бидејќи е вклучен „Штедач на батерија“. Ова може да се исклучи во „Поставки“."</string> - <string name="device_state_notification_settings_button" msgid="691937505741872749">"Одете во „Поставките“"</string> + <string name="device_state_notification_settings_button" msgid="691937505741872749">"Отворете „Поставки“"</string> <string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"Исклучи"</string> <string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> е конфигуриран"</string> <string name="keyboard_layout_notification_one_selected_message" msgid="4314216053129257197">"Распоредот на тастатурата е поставен на <xliff:g id="LAYOUT_1">%s</xliff:g>. Допрете за да промените."</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 23e3b3a4fbaf..5fa3b52d7000 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> ഉപകരണത്തിൽ ഇത് ആക്സസ് ചെയ്യാനാകില്ല. പകരം നിങ്ങളുടെ ഫോണിൽ ശ്രമിച്ച് നോക്കൂ."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ഈ ആപ്പ് Android-ന്റെ പഴയ പതിപ്പിനായാണ് സൃഷ്ടിച്ചിരിക്കുന്നത്. ഇത് ശരിയായി പ്രവർത്തിക്കണമെന്നില്ല, ഏറ്റവും പുതിയ സുരക്ഷാ, സ്വകാര്യതാ പരിരക്ഷകൾ ഇതിൽ ഉൾപ്പെടുന്നുമില്ല. അപ്ഡേറ്റിനായി തിരയുക അല്ലെങ്കിൽ ആപ്പിന്റെ ഡെവലപ്പറെ ബന്ധപ്പെടുക."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"അപ്ഡേറ്റിനായി പരിശോധിക്കുക"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ഈ ആപ്പ്, Android-ന്റെ എറ്റവും പുതിയ പതിപ്പിന് അനുയോജ്യമല്ല. അപ്ഡേറ്റിന് തിരയുക അല്ലെങ്കിൽ ആപ്പിന്റെ ഡെവലപ്പറെ ബന്ധപ്പെടുക."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"നിങ്ങൾക്ക് പുതിയ സന്ദേശങ്ങൾ ഉണ്ട്"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"കാണുന്നതിന് SMS ആപ്പ് തുറക്കുക"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ചില പ്രവർത്തനം പരിമിതപ്പെടാം"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 00c62404ab2b..204cf39de5f6 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Үүнд таны <xliff:g id="DEVICE">%1$s</xliff:g>-с хандах боломжгүй. Оронд нь утсан дээрээ туршиж үзнэ үү."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Энэ аппыг Android-н хуучин хувилбарт зориулж бүтээсэн. Энэ нь зохих ёсоор ажиллахгүй байж магадгүй бөгөөд хамгийн сүүлийн үеийн аюулгүй байдал болон нууцлалын хамгаалалтыг агуулдаггүй. Шинэчлэлт байгаа эсэхийг шалгах эсвэл аппын хөгжүүлэгчтэй холбогдоно уу."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Шинэчлэлтийг шалгах"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Энэ апп Android-н хамгийн сүүлийн үеийн хувилбартай тохиромжгүй. Шинэчлэлт байгаа эсэхийг шалгах эсвэл аппын хөгжүүлэгчтэй холбогдоно уу."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Танд шинэ мессежүүд байна"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Үзэхийн тулд SMS аппыг нээх"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Зарим функцийг хязгаарласан байж болзошгүй"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index c74a1ca59ce0..c728050bfa2b 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"रंग सुधारणा"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एकहाती मोड"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"आणखी डिम"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"श्रवणयंत्र डिव्हाइस"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"श्रवणयंत्रे"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"व्हॉल्यूम की रिलीझ करा. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू करण्यासाठी, दोन्ही व्हॉल्यूम की पुन्हा प्रेस करा आणि तीन सेकंदांसाठी धरून ठेवा."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"हे तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वर अॅक्सेस केले जाऊ शकत नाही. त्याऐवजी तुमच्या फोनवर अॅक्सेस करून पहा."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"हे ॲप Android च्या जुन्या आवृत्तीसाठी तयार केले होते. ते कदाचित व्यवस्थित काम करणार नाही आणि त्यामध्ये सुरक्षा व गोपनीयतेशी संबंधित नवीनतम संरक्षणे समाविष्ट नाहीत. अपडेटसाठी तपासा किंवा अॅपच्या डेव्हलपरशी संपर्क साधा."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेटसाठी तपासा"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"हे ॲप Android च्या नवीनतम आवृत्तीशी कंपॅटिबल नाही. अपडेटसाठी तपासा किंवा अॅपच्या डेव्हलपरशी संपर्क साधा."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"आपल्याकडे नवीन मेसेज आहेत"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"पाहण्यासाठी SMS अॅप उघडा"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"काही कार्यक्षमता मर्यादित असू शकतात"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index e8c5ab9a1058..5cb9c09890fe 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1696,7 +1696,7 @@ <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"HIDUP"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"MATI"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Benarkan <xliff:g id="SERVICE">%1$s</xliff:g> mempunyai kawalan penuh atas peranti anda?"</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"Kawalan penuh sesuai untuk apl yang membantu anda dengan keperluan kebolehaksesan tetapi bukan untuk kebanyakan apl."</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"Kawalan penuh sesuai untuk apl yang membantu anda berkaitan dengan keperluan kebolehaksesan tetapi bukan untuk kebanyakan apl."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Melihat dan mengawal skrin"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ciri ini boleh membaca semua kandungan pada skrin dan memaparkan kandungan di atas apl lain."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Lihat dan laksanakan tindakan"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Apl ini tidak boleh diakses pada <xliff:g id="DEVICE">%1$s</xliff:g> anda. Cuba pada telefon anda."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Apl ini dibina untuk versi Android yang lebih lama. Apl ini mungkin tidak berfungsi dengan betul dan tidak termasuk perlindungan keselamatan dan privasi yang terkini. Semak kemas kini atau hubungi pembangun apl."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Semak kemaskinian"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Apl ini tidak serasi dengan versi terbaharu Android. Semak kemaskinian atau hubungi pembangun apl."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Anda mempunyai mesej baharu"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Buka apl SMS untuk melihat"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Sesetengah fungsi mungkin terhad"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index e206ad389d0f..d90c5b825537 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -681,8 +681,8 @@ <string name="face_acquired_too_dark" msgid="8539853432479385326">"အလင်းရောင် အားနည်းသည်"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"ဖုန်းကို အဝေးသို့ခွာပါ"</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"ဖုန်းကို အနားသို့ပိုတိုးပါ"</string> - <string name="face_acquired_too_high" msgid="8278815780046368576">"ဖုန်းကို ပိုမြှင့်လိုက်ပါ"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"ဖုန်းကို ပိုနှိမ့်လိုက်ပါ"</string> + <string name="face_acquired_too_high" msgid="8278815780046368576">"ဖုန်းကို မြှင့်လိုက်ပါ"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"ဖုန်းကို နှိမ့်လိုက်ပါ"</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"ဖုန်းကို သင့်ဘယ်ဘက်သို့ ရွှေ့ပါ"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"ဖုန်းကို သင့်ညာဘက်သို့ ရွှေ့ပါ"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"သင့်စက်ပစ္စည်းကို တည့်တည့်ကြည့်ပါ။"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"၎င်းကို သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> တွင် သုံး၍မရပါ။ ဖုန်းတွင် စမ်းကြည့်ပါ။"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ဤအက်ပ်ကို ဗားရှင်းဟောင်းအတွက် ရေးထားသည်။ ၎င်းကမှန်ကန်စွာ လုပ်ဆောင်နိုင်မည်မဟုတ်ဘဲ နောက်ဆုံးထုတ် လုံခြုံရေးနှင့် ကိုယ်ရေးအချက်အလက်လုံခြုံမှု အကာအကွယ်များ မပါဝင်ပါ။ အပ်ဒိတ်ရှာကြည့်ပါ (သို့) အက်ပ်ရေးသူထံ ဆက်သွယ်ပါ။"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"အပ်ဒိတ်စစ်ရန်"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ဤအက်ပ်သည် Android နောက်ဆုံးဗားရှင်းနှင့် တွဲမသုံးနိုင်ပါ။ အပ်ဒိတ်ရှာကြည့်ပါ (သို့) အက်ပ်ရေးသူထံ ဆက်သွယ်ပါ။"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"သင့်ထံတွင် စာအသစ်များရောက်နေသည်"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"ကြည့်ရှုရန် SMS အက်ပ်ကိုဖွင့်ပါ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"လုပ်ဆောင်ချက် ကန့်သတ်မှုရှိနိုင်သည်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index a2102bcd6688..845eb92dc559 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Dette er ikke tilgjengelig på <xliff:g id="DEVICE">%1$s</xliff:g>. Prøv på telefonen din i stedet."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Denne appen er utviklet for en eldre Android-versjon. Det er ikke sikkert at den fungerer som den skal, og den mangler det nyeste innen sikkerhet og personvern. Se etter en oppdatering, eller kontakt utvikleren av appen."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Se etter oppdateringer"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Denne appen er ikke kompatibel med den nyeste versjonen av Android. Se etter en oppdatering, eller kontakt utvikleren av appen."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nye meldinger"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Åpne SMS-appen for å se"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Enkelte funksjoner kan begrenses"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 83cde3c7ccaf..e7de30871a0d 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -206,7 +206,7 @@ <string name="printing_disabled_by" msgid="3517499806528864633">"<xliff:g id="OWNER_APP">%s</xliff:g> ले छाप्ने कार्यलाई असक्षम पार्यो।"</string> <string name="personal_apps_suspension_title" msgid="7561416677884286600">"आफ्नो कार्य प्रोफाइल अन गर्नुहोस्"</string> <string name="personal_apps_suspension_text" msgid="6115455688932935597">"तपाईंले आफ्नो कार्य प्रोफाइल सक्रिय नगरुन्जेल तपाईंका व्यक्तिगत एपहरूलाई रोक लगाइन्छ"</string> - <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"मिति <xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> बजे व्यक्तिगत एपहरूलाई रोक लगाइने छ। तपाईंका IT एडमिन तपाईंलाई आफ्नो कार्य प्रोफाइल <xliff:g id="NUMBER">%3$d</xliff:g> भन्दा धेरै दिन निष्क्रिय राख्ने अनुमति दिनुहुन्न।"</string> + <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"मिति <xliff:g id="DATE">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> बजे व्यक्तिगत एपहरूलाई रोक लगाइने छ। तपाईंका IT एड्मिन तपाईंलाई आफ्नो कार्य प्रोफाइल <xliff:g id="NUMBER">%3$d</xliff:g> भन्दा धेरै दिन निष्क्रिय राख्ने अनुमति दिनुहुन्न।"</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"अन गर्नुहोस्"</string> <string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"कल वा म्यासेज अफ गरिएको छ"</string> <string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"तपाईंले कामसम्बन्धी एपहरू पज गर्नुभएको छ। तपाईं फोन कल वा टेक्स्ट म्यासेजहरू प्राप्त गर्नु हुने छैन।"</string> @@ -995,7 +995,7 @@ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="8381565919325410939">"SIM कार्ड अनलक गरिँदै छ…"</string> <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"तपाईँले तपाईँको अनलक प्याटर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string> <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"तपाईंले गलत तरिकाले आफ्नो पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नुभयो। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> - <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> + <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत PIN टाइप गर्नुभएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"तपाईँले तपाईँको अनलक प्याटर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> थप असफल कोसिसहरू, तपाईँको Google साइन इन प्रयोग गरी तपाईँको ट्याब्लेट अनलक गर्न भनिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा फरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"तपाईंले आफ्नो अनलक शैली <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले कोर्नुभएको छ। थप <xliff:g id="NUMBER_1">%2$d</xliff:g> प्रयासहरू असफल भएपछि तपाईंलाई आफ्नो Google खाता मार्फत साइन इन गरेर आफ्नो Android टिभी डिभाइस अनलक गर्न अनुरोध गरिने छ।\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डपछि फेरि प्रयास गर्नुहोस्।"</string> <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"तपाईँले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले तपाईँको अनलक ढाँचालाई कोर्नु भएको छ। पछि <xliff:g id="NUMBER_1">%2$d</xliff:g> अरू धेरै असफल कोसिसहरूपछि, तपाईँलाई तपाईँको फोन Google साइन इन प्रयोग गरेर अनलक गर्नको लागि सोधिने छ। \n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> सेकेन्डमा पुनः प्रयास गर्नुहोस्।"</string> @@ -1633,7 +1633,7 @@ <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"सेटिंङहरू"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"डिस्कनेक्ट गर्नुहोस्"</string> <string name="media_route_status_scanning" msgid="8045156315309594482">"स्क्यान गर्दै ..."</string> - <string name="media_route_status_connecting" msgid="5845597961412010540">"जडान हुँदै..."</string> + <string name="media_route_status_connecting" msgid="5845597961412010540">"कनेक्ट गरिँदै छ..."</string> <string name="media_route_status_available" msgid="1477537663492007608">"उपलब्ध"</string> <string name="media_route_status_not_available" msgid="480912417977515261">"उपलब्ध छैन"</string> <string name="media_route_status_in_use" msgid="6684112905244944724">"प्रयोगमा छ"</string> @@ -1667,7 +1667,7 @@ <string name="kg_login_invalid_input" msgid="8292367491901220210">"अमान्य प्रयोगकर्तानाम वा पासवर्ड।"</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"के तपाईँले उपयोगकर्ता नाम वा पासवर्ड बिर्सनुभयो?\n"<b>"google.com/accounts/recovery"</b>" मा जानुहोस्।"</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"खाता जाँच हुँदै…"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"तपाईँले गलत तरिकाले तपाईँको PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक टाइप गर्नु भएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत PIN टाइप गर्नुभएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"तपाईँले तपाईँक पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत टाइप गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"तपाईँले तपाईँको अनलक प्याटर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक खिच्नु भएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"तपाईँले ट्याब्लेटलाई अनलक गर्न गलत तरिकाले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक कोसिस गर्नु भएको छ। <xliff:g id="NUMBER_1">%2$d</xliff:g> पछि थप असफल प्रयासहरू, ट्याब्लेट डिफल्ट कार्यशालामा रिसेट गरिने छ र सबै प्रयोग डेटा हराउने छ।"</string> @@ -1696,7 +1696,7 @@ <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"सक्रिय"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"निष्क्रिय"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> लाई तपाईंको यन्त्र पूर्ण रूपमा नियन्त्रण गर्न दिने हो?"</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"एक्सेसिबिलिटीसम्बन्धी आवश्यकतामा सहयोग गर्ने एपको पूर्ण नियन्त्रण गर्नु उपयुक्त हुन्छ तर अधिकांश एपका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"एक्सेसिबिलिटीसम्बन्धी आवश्यकतामा सहयोग गर्ने एपको पूर्ण नियन्त्रण गर्न दिनु उपयुक्त हुन्छ तर अधिकांश एपका हकमा यस्तो नियन्त्रण उपयुक्त हुँदैन।"</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रिन हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"यसले स्क्रिनमा देखिने सबै सामग्री पढ्न सक्छ र अन्य एपहरूमा उक्त सामग्री देखाउन सक्छ।"</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"कारबाहीहरू हेर्नुहोस् र तिनमा कार्य गर्नुहोस्"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मा यो एप चलाउन मिल्दैन। बरु तपाईंको फोनमा स्ट्रिम गरी हेर्नुहोस्।"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"यो एप Android को पुरानो संस्करणका लागि निर्माण गरिएको थियो। यो एपले राम्ररी काम नगर्न सक्छ र यसमा सुरक्षा तथा गोपनीयतासम्बन्धी पछिल्ला सुरक्षा सुविधाहरू समावेश नहुन सक्छन्। यस एपको नवीनतम संस्करण उपलब्ध छ कि छैन हेर्नुहोस् वा एपका विकासकर्तासँग सम्पर्क गर्नुहोस्।"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेट उपलब्ध छ वा छैन जाँच्नुहोस्"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"यो एप Android को नवीनतम संस्करणसँग कम्प्याटिबल छैन। यस एपको नवीनतम संस्करण उपलब्ध छ कि छैन भन्ने कुरा हेर्नुहोस् वा एपका विकासकर्तालाई सम्पर्क गर्नुहोस्।"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"तपाईंलाई नयाँ सन्देश आएको छ"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"हेर्नका लागि SMS एप खोल्नुहोस्"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"केही सुविधा राम्ररी नचल्न सक्छन्"</string> @@ -2167,8 +2168,8 @@ <string name="miniresolver_open_in_work" msgid="941341494673509916">"कामसम्बन्धी <xliff:g id="APP">%s</xliff:g> मा खोल्ने हो?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"कामसम्बन्धी एपबाट कल गर्ने हो?"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"कामसम्बन्धी एप प्रयोग गर्ने हो?"</string> - <string name="miniresolver_call_information" msgid="6739417525304184083">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र कल गर्ने अनुमति दिन्छ"</string> - <string name="miniresolver_sms_information" msgid="4311292661329483088">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र म्यासेज पठाउने अनुमति दिन्छ"</string> + <string name="miniresolver_call_information" msgid="6739417525304184083">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र कल गर्ने अनुमति दिएको छ"</string> + <string name="miniresolver_sms_information" msgid="4311292661329483088">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र म्यासेज पठाउने अनुमति दिएको छ"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"व्यक्तिगत ब्राउजर प्रयोग गर्नुहोस्"</string> <string name="miniresolver_use_work_browser" msgid="543575306251952994">"कार्य ब्राउजर प्रयोग गर्नुहोस्"</string> <string name="miniresolver_call" msgid="6386870060423480765">"कल गर्नुहोस्"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 345807b7300d..f2b03086ea94 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Je hebt hier geen toegang toe op je <xliff:g id="DEVICE">%1$s</xliff:g>. Probeer het in plaats daarvan op je telefoon."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Deze app is ontworpen voor een oudere versie van Android. De app werkt misschien niet goed en bevat niet de nieuwste beveiligings- en privacybeschermingsopties. Check op een update of neem contact op met de ontwikkelaar van de app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Controleren op updates"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Deze app is niet compatibel met de nieuwste versie van Android. Check of er updates zijn of neem contact op met de ontwikkelaar van de app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Je hebt nieuwe berichten"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Open je sms-app om ze te bekijken"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Functionaliteit kan beperkt zijn"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index cf8aa30a2d8a..5d1de82e71f5 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ଏହାକୁ ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରେ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ଆପଣଙ୍କ ଫୋନରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ଏହି ଆପକୁ Androidର ଏକ ପୁରୁଣା ସଂସ୍କରଣ ପାଇଁ ତିଆରି କରାଯାଇଛି। ଏହା ସଠିକ୍ ଭାବେ କାମ କରିନପାରେ ଏବଂ ଏଥିରେ ନବୀନତମ ସୁରକ୍ଷା ଏବଂ ଗୋପନୀୟତା ସୁରକ୍ଷାଗୁଡ଼ିକ ଅନ୍ତର୍ଭୁକ୍ତ ନାହିଁ। ଏକ ଅପଡେଟ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ କିମ୍ବା ଆପର ଡେଭେଲପରଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ଅପଡେଟ୍ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ଏହି ଆପ Androidର ନବୀନତମ ଭର୍ସନ ସହ କମ୍ପାଟିବଲ ନୁହେଁ। ଏକ ଅପଡେଟ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ କିମ୍ବା ଆପର ଡେଭେଲପରଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"ଆପଣଙ୍କ ପାଖରେ ନୂଆ ମେସେଜ୍ ରହିଛି"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"ଦେଖିବା ପାଇଁ SMS ଆପ୍ ଖୋଲନ୍ତୁ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ହୁଏତ କିଛି ପ୍ରକାର୍ଯ୍ୟ ସୀମିତ ହୋଇପାରେ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 9c083fa2b4f4..25f7f7cf6976 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1633,7 +1633,7 @@ <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"ਸੈਟਿੰਗਾਂ"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"ਡਿਸਕਨੈਕਟ ਕਰੋ"</string> <string name="media_route_status_scanning" msgid="8045156315309594482">"ਸਕੈਨ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string> - <string name="media_route_status_connecting" msgid="5845597961412010540">"ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string> + <string name="media_route_status_connecting" msgid="5845597961412010540">"ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string> <string name="media_route_status_available" msgid="1477537663492007608">"ਉਪਲਬਧ"</string> <string name="media_route_status_not_available" msgid="480912417977515261">"ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="media_route_status_in_use" msgid="6684112905244944724">"ਵਰਤੋਂ ਵਿੱਚ"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> \'ਤੇ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ਇਹ ਐਪ Android ਦੇ ਕਿਸੇ ਪੁਰਾਣੇ ਵਰਜਨ ਲਈ ਬਣਾਈ ਗਿਆ ਸੀ। ਸ਼ਾਇਦ ਇਹ ਠੀਕ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ ਅਤੇ ਨਵੀਨਤਮ ਸੁਰੱਖਿਆ ਅਤੇ ਪਰਦੇਦਾਰੀ ਸੰਬੰਧੀ ਸੁਰੱਖਿਆਵਾਂ ਸ਼ਾਮਲ ਨਾ ਹੋਣ। ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ ਜਾਂ ਐਪ ਦੇ ਵਿਕਾਸਕਾਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ਇਹ ਐਪ Android ਦੇ ਨਵੀਨਤਮ ਵਰਜਨ ਦੇ ਅਨੁਰੂਪ ਨਹੀਂ ਹੈ। ਅੱਪਡੇਟ ਲਈ ਜਾਂਚ ਕਰੋ ਜਾਂ ਐਪ ਦੇ ਵਿਕਾਸਕਾਰ ਨੂੰ ਸੰਪਰਕ ਕਰੋ।"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"ਤੁਹਾਨੂੰ ਨਵੇਂ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਹੋਏ ਹਨ"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"ਦੇਖਣ ਲਈ SMS ਐਪ ਖੋਲ੍ਹੋ"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ਕੁਝ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਸੀਮਤ ਹੋ ਸਕਦੀ ਹੈ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 46202c91d7f0..9871ccbccaa0 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -688,7 +688,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"Przesuń telefon w lewo"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Przesuń telefon w prawo"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Patrz prosto na urządzenie."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"Nie widać twarzy. Trzymaj telefon na wysokości oczu."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"Nie widać twarzy – trzymaj telefon na wysokości oczu"</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Telefon się porusza. Trzymaj go nieruchomo."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Zarejestruj swoją twarz ponownie."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"Nie rozpoznaję twarzy. Spróbuj ponownie."</string> @@ -704,7 +704,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Nie można utworzyć modelu twarzy. Spróbuj ponownie."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Wykryto ciemne okulary. Twarz musi być widoczna w całości."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Wykryto zasłonę twarzy. Twarz musi być widoczna w całości."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Wykryto zasłonę twarzy – twarz musi być widoczna w całości"</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Nie można zweryfikować twarzy. Sprzęt niedostępny."</string> @@ -967,7 +967,7 @@ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Naciśnij Menu, aby odblokować lub wykonać połączenie alarmowe."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Naciśnij Menu, aby odblokować."</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Narysuj wzór, aby odblokować"</string> - <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Alarmowe"</string> + <string name="lockscreen_emergency_call" msgid="7500692654885445299">"Funkcje alarmowe"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"Powrót do połączenia"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Poprawnie!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Spróbuj ponownie."</string> @@ -1669,8 +1669,8 @@ <string name="kg_login_invalid_input" msgid="8292367491901220210">"Nieprawidłowa nazwa użytkownika lub hasło."</string> <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Nie pamiętasz nazwy użytkownika lub hasła?\nWejdź na "<b>"google.com/accounts/recovery"</b>"."</string> <string name="kg_login_checking_password" msgid="4676010303243317253">"Sprawdzam konto"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Wpisałeś nieprawidłowy kod PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> wpisałeś nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> został wpisany nieprawidłowy kod PIN. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> zostało wpisane nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Narysowałeś nieprawidłowy wzór odblokowania <xliff:g id="NUMBER_0">%1$d</xliff:g> razy. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> próbowałeś nieprawidłowo odblokować tablet. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach tablet zostanie zresetowany do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string> <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Próbujesz odblokować urządzenie z Androidem TV w nieprawidłowy sposób. To była <xliff:g id="NUMBER_0">%1$d</xliff:g> próba. Po kolejnych <xliff:g id="NUMBER_1">%2$d</xliff:g> nieudanych próbach urządzenie zostanie zresetowane do ustawień fabrycznych, a wszystkie dane użytkownika zostaną utracone."</string> @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nie można z tego skorzystać na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>. Użyj telefonu."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ta aplikacja jest na starszą wersję Androida. Może nie działać prawidłowo i nie uwzględnia najnowszych zabezpieczeń oraz ustawień ochrony prywatności. Sprawdź, czy możesz ją zaktualizować, lub skontaktuj się z deweloperem."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Sprawdź dostępność aktualizacji"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ta aplikacja nie jest zgodna z najnowszą wersją Androida. Sprawdź, czy możesz ją zaktualizować, lub skontaktuj się z deweloperem."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Masz nowe wiadomości"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Otwórz aplikację do SMS-ów, by wyświetlić wiadomość"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Część funkcji może być niedostępnych"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index ced295833d94..2ce7094055ed 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo smartphone."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Este app foi criado para uma versão mais antiga do Android. Ele pode não funcionar corretamente e não inclui as proteções de privacidade e segurança mais recentes. Verifique se há uma atualização ou entre em contato com o desenvolvedor do app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Procurar atualizações"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Este app não é compatível com a versão mais recente do Android. Verifique se há uma atualização ou entre em contato com o desenvolvedor do app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Você tem mensagens novas"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Abra o app de SMS para ver"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Algumas funções são limitadas"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 3511eac680d9..ea30d7c3e322 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível aceder a esta app no seu dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>. Em alternativa, experimente no telemóvel."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Esta app foi criada para uma versão mais antiga do Android. Pode não funcionar corretamente e não inclui as proteções de privacidade e segurança mais recentes. Verifique se existem atualizações ou contacte o programador da app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Rever atualizações"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Esta app não é compatível com a versão mais recente do Android. Verifique se existem atualizações ou contacte o programador da app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Tem mensagens novas"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Abra a app de SMS para ver"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Algumas funcionalidades limitadas"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index ced295833d94..2ce7094055ed 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Não é possível acessar essa configuração pelo seu <xliff:g id="DEVICE">%1$s</xliff:g>. Tente pelo smartphone."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Este app foi criado para uma versão mais antiga do Android. Ele pode não funcionar corretamente e não inclui as proteções de privacidade e segurança mais recentes. Verifique se há uma atualização ou entre em contato com o desenvolvedor do app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Procurar atualizações"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Este app não é compatível com a versão mais recente do Android. Verifique se há uma atualização ou entre em contato com o desenvolvedor do app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Você tem mensagens novas"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Abra o app de SMS para ver"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Algumas funções são limitadas"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index d4bddd3dbef7..e103148cba3b 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe telefon."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Aplicația a fost creată pentru o versiune mai veche de Android. Poate să nu funcționeze corect și nu include cele mai recente măsuri de protecție a securității și a confidențialității. Caută o actualizare sau contactează dezvoltatorul aplicației."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Caută actualizări"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Aplicația nu este compatibilă cu cea mai recentă versiune de Android. Caută o actualizare sau contactează dezvoltatorul aplicației."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Ai mesaje noi"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Deschide aplicația pentru SMS-uri ca să vezi"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Unele funcții ar putea fi limitate"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index fb1d5c6c4418..cbdd5cb6cc86 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"На устройстве <xliff:g id="DEVICE">%1$s</xliff:g> эта функция недоступна. Используйте телефон."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Это приложение было разработано для более ранней версии Android. Оно не соответствует последним требованиям к обеспечению конфиденциальности и безопасности данных и может работать некорректно. Проверьте наличие обновлений или свяжитесь с разработчиком приложения."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверить обновления"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Это приложение не совместимо с последней версией Android. Проверьте наличие обновлений или свяжитесь с разработчиком приложения."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Новые сообщения"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Чтобы просмотреть, откройте приложение для обмена SMS"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Некоторые функции могут не работать"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index f1636dbe0825..7358a9acb3a2 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"මෙයට ඔබේ <xliff:g id="DEVICE">%1$s</xliff:g> මත ප්රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ දුරකථනයෙහි උත්සාහ කරන්න."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"මෙම යෙදුම Android හි පැරණි අනුවාදයක් සඳහා තනා ඇත. එය නිසි ලෙස ක්රියා නොකරන අතර නවතම ආරක්ෂාව සහ රහස්යතා ආරක්ෂාව ඇතුළත් නොවේ. යාවත්කාලීනයක් සඳහා පරීක්ෂා කරන්න, නැතහොත් යෙදුමේ සංවර්ධකයා සම්බන්ධ කර ගන්න."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"යාවත්කාලීන සඳහා පරික්ෂා කරන්න"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"මෙම යෙදුම Android හි නවතම අනුවාදය සමග නොගැළපෙයි. යාවත්කාලීනයක් සඳහා පරීක්ෂා කරන්න නැතහොත් යෙදුමේ සංවර්ධකයා සම්බන්ධ කර ගන්න."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"ඔබට නව පණිවිඩ තිබේ"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"බැලීමට විවෘත SMS යෙදුම විවෘත කරන්න"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"ඇතැම් ක්රියාකාරිත්ව සීමිත විය හැක"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index f9c2a9b3dcb4..68f0ed4aa429 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> momentálne nemáte prístup k tomuto obsahu. Skúste použiť telefón."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Táto aplikácia bola vytvorená pre staršiu verziu Androidu. Nemusí správne fungovať a obsahovať najnovšie prvky zabezpečenia a ochrany súkromia. Skontrolujte dostupnosť aktualizácie alebo kontaktujte jej vývojára."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Skontrolovať dostupnosť aktualizácie"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Táto aplikácia nie je kompatibilná s najnovšou verziou Androidu. Skontrolujte dostupnosť aktualizácie alebo kontaktujte jej vývojára."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Máte nové správy."</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Otvorte aplikáciu pre SMS a zobrazte správu"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Niektoré funkcie môžu byť obmedzené"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index a661ad8b7543..90fa4ae188aa 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1702,7 +1702,7 @@ <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ogledovanje in upravljanje zaslona"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Bere lahko vso vsebino na zaslonu ter prikaže vsebino prek drugih aplikacij."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Ogledovanje in izvajanje dejanj"</string> - <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Spremlja lahko vaše interakcije z aplikacijo ali senzorjem strojne opreme ter komunicira z aplikacijami v vašem imenu."</string> + <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Spremlja lahko vaše interakcije z aplikacijo ali tipalom strojne opreme ter komunicira z aplikacijami v vašem imenu."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dovoli"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zavrni"</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Če želite začeti uporabljati funkcijo, se je dotaknite:"</string> @@ -1717,7 +1717,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"Popravljanje barv"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enoročni način"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Zelo zatemnjen zaslon"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni aparati"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni pripomočki"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Spustite gumba za glasnost. Če želite vklopiti storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znova pritisnite in 3 sekunde pridržite oba gumba za glasnost."</string> @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"V napravi <xliff:g id="DEVICE">%1$s</xliff:g> ni mogoče dostopati do te vsebine. Poskusite s telefonom."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ta aplikacija je bila razvita za starejšo različico Androida. Morda ne bo delovala pravilno ter ne vključuje najnovejših varnostnih funkcij in funkcij za varovanje zasebnosti. Preverite, ali je na voljo posodobitev, ali pa se obrnite na razvijalca aplikacije."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Preveri, ali je na voljo posodobitev"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ta aplikacija ni združljiva z najnovejšo različico Androida. Preverite, ali je na voljo posodobitev, ali pa se obrnite na razvijalca aplikacije."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nova sporočila."</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Za ogled odprite aplikacijo za SMS-je"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Nekatere funkcije bodo morda omejene"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 6cbbd28e97dc..2ad8a50f412b 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -155,7 +155,7 @@ <string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nuk u transferua"</string> <string name="fcComplete" msgid="1080909484660507044">"Kodi i funksionit është i plotë."</string> <string name="fcError" msgid="5325116502080221346">"Problem me lidhjen ose kod është i pavlefshëm."</string> - <string name="httpErrorOk" msgid="6206751415788256357">"Në rregull!"</string> + <string name="httpErrorOk" msgid="6206751415788256357">"Në rregull"</string> <string name="httpError" msgid="3406003584150566720">"Pati një gabim në rrjet."</string> <string name="httpErrorLookup" msgid="3099834738227549349">"Nuk mundi ta gjente URL-në."</string> <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"Skema e vërtetimit nuk mbështetet."</string> @@ -1698,9 +1698,9 @@ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Do të lejosh që <xliff:g id="SERVICE">%1$s</xliff:g> të ketë kontroll të plotë të pajisjes sate?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"Kontrolli i plotë është i përshtatshëm për aplikacionet që të ndihmojnë me nevojat e qasshmërisë, por jo për shumicën e aplikacioneve."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Shiko dhe kontrollo ekranin"</string> - <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ai mund të lexojë të gjithë përmbajtjen në ekran dhe të shfaqë përmbajtjen mbi aplikacione të tjera."</string> + <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Mund të lexojë të gjithë përmbajtjen në ekran dhe të shfaqë përmbajtjen mbi aplikacione të tjera."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Shiko dhe kryej veprimet"</string> - <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Ai mund të monitorojë ndërveprimet me një aplikacion ose një sensor hardueri dhe të ndërveprojë me aplikacionet në emrin tënd."</string> + <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Mund të monitorojë ndërveprimet me një aplikacion ose një sensor hardueri dhe të ndërveprojë me aplikacionet në emrin tënd."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Lejo"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuzo"</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Trokit te një veçori për të filluar ta përdorësh atë:"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Qasja është e pamundur në <xliff:g id="DEVICE">%1$s</xliff:g>. Provoje në telefon më mirë."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ky aplikacion është krijuar për një version më të vjetër të Android. Mund të mos funksionojë siç duhet dhe nuk përfshin mbrojtjet më të fundit të sigurisë dhe privatësisë. Kontrollo për një përditësim ose kontakto zhvilluesin e aplikacionit."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kontrollo për përditësim"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ky aplikacion nuk është i përputhshëm me versionin më të fundit të Android. Kontrollo për një përditësim ose kontakto me zhvilluesin e aplikacionit."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Ke mesazhe të reja"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Hap aplikacionin SMS për ta parë"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Disa veçori mund të jenë të kufizuara"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 9753ee04e135..ab47275db2b8 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -86,7 +86,7 @@ <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Пробајте да промените жељену мрежу. Додирните да бисте променили."</string> <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Хитни позиви нису доступни"</string> <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Не можете да упућујете хитне позиве преко Wi‑Fi-ја"</string> - <string name="notification_channel_network_alert" msgid="4788053066033851841">"Обавештења"</string> + <string name="notification_channel_network_alert" msgid="4788053066033851841">"Упозорења"</string> <string name="notification_channel_call_forward" msgid="8230490317314272406">"Преусмеравање позива"</string> <string name="notification_channel_emergency_callback" msgid="54074839059123159">"Режим за хитан повратни позив"</string> <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Статус мобилних података"</string> @@ -284,7 +284,7 @@ <string name="notification_channel_network_available" msgid="6083697929214165169">"Мрежа је доступна"</string> <string name="notification_channel_vpn" msgid="1628529026203808999">"Статус VPN-а"</string> <string name="notification_channel_device_admin" msgid="6384932669406095506">"Обавештења од ИТ администратора"</string> - <string name="notification_channel_alerts" msgid="5070241039583668427">"Обавештења"</string> + <string name="notification_channel_alerts" msgid="5070241039583668427">"Упозорења"</string> <string name="notification_channel_retail_mode" msgid="3732239154256431213">"Режим демонстрације за малопродајне објекте"</string> <string name="notification_channel_usb" msgid="1528280969406244896">"USB веза"</string> <string name="notification_channel_heavy_weight_app" msgid="17455756500828043">"Активна апликација"</string> @@ -1976,6 +1976,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Овој апликацији не може да се приступи са уређаја <xliff:g id="DEVICE">%1$s</xliff:g>. Пробајте на телефону."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ова апликација је направљена за старију верзију Android-а. Можда неће радити исправно и не обухвата најновије безбедносне функције и заштите приватности. Проверите да ли има ажурирања или се обратите програмеру апликације."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Потражи ажурирање"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ова апликација није компатибилна са најновијом верзијом Android-а. Проверите да ли има ажурирања или се обратите програмеру апликације."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нове поруке"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Отворите апликацију за SMS да бисте прегледали"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Неке функције су можда ограничене"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 6da10ecb1d2e..815c9a94193e 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"Färgkorrigering"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhandsläge"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörapparater"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörhjälpmedel"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Släpp volymknapparna. Du kan aktivera <xliff:g id="SERVICE_NAME">%1$s</xliff:g> genom att hålla båda volymknapparna nedtryckta i tre sekunder igen."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Det går inte att streama detta till <xliff:g id="DEVICE">%1$s</xliff:g>. Testa med telefonen i stället."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Den här appen byggdes för en äldre version av Android. Den kanske inte fungerar som den ska och har inte de senaste säkerhets- och integritetsskydden. Sök efter en uppdatering eller kontakta appens utvecklare."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Sök efter uppdateringar"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Denna app är inte kompatibel med den senaste versionen av Android. Sök efter en uppdatering eller kontakta appens utvecklare."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nya meddelanden"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Öppna sms-appen och visa meddelandet"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Vissa funktioner är begränsade"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 2fb0d1710bcc..d2766803787a 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -702,7 +702,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Imeshindwa kuunda muundo wa uso wako. Jaribu tena."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Vioo vyeusi vimetambuliwa. Ni lazima uso wako wote uonekane."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Kifuniko cha uso kimetambuliwa. Ni lazima uso wako wote uonekane."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Uso umefunikwa. Lazima uso wako wote uonekane."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Imeshindwa kuthibitisha uso. Maunzi hayapatikani."</string> @@ -839,7 +839,7 @@ <string-array name="phoneTypes"> <item msgid="8996339953292723951">"Ya nyumbani"</item> <item msgid="7740243458912727194">"Simu ya mkononi"</item> - <item msgid="8526146065496663766">"Ya kazini"</item> + <item msgid="8526146065496663766">"Kazini"</item> <item msgid="8150904584178569699">"Pepesi ya Kazini"</item> <item msgid="4537253139152229577">"Pepesi ya Nyumbani"</item> <item msgid="6751245029698664340">"Kurasa anwani"</item> @@ -882,7 +882,7 @@ <string name="phoneTypeCustom" msgid="5120365721260686814">"Maalum"</string> <string name="phoneTypeHome" msgid="3880132427643623588">"Ya nyumbani"</string> <string name="phoneTypeMobile" msgid="1178852541462086735">"Simu ya mkononi"</string> - <string name="phoneTypeWork" msgid="6604967163358864607">"Ya kazini"</string> + <string name="phoneTypeWork" msgid="6604967163358864607">"Kazini"</string> <string name="phoneTypeFaxWork" msgid="6757519896109439123">"Pepesi ya Kazini"</string> <string name="phoneTypeFaxHome" msgid="6678559953115904345">"Pepesi ya Nyumbani"</string> <string name="phoneTypePager" msgid="576402072263522767">"Peja"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Huwezi kufikia mipangilio hii kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako. Badala yake jaribu kwenye simu yako."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Programu hii ilisanidiwa kwa ajili ya toleo la zamani la Android. Huenda isifanye kazi ipasavyo na haijumuishi ulinzi wa faragha na usalama wa hivi karibuni. Angalia kama ina sasisho au wasiliana na msanidi wa programu."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Angalia masasisho"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Programu hii haitumiki kwenye toleo jipya zaidi la Android. Angalia iwapo sasisho linapatikana au wasiliana na msanidi programu."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Una ujumbe mpya"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Fungua programu ya SMS ili uweze kuangalia"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Huenda baadhi ya vipengele vinadhibitiwa"</string> @@ -2166,7 +2167,7 @@ <string name="miniresolver_open_in_personal" msgid="807427577794490375">"Ungependa kufungua <xliff:g id="APP">%s</xliff:g> ukitumia wasifu wa binafsi?"</string> <string name="miniresolver_open_in_work" msgid="941341494673509916">"Ungependa kufungua <xliff:g id="APP">%s</xliff:g> ukitumia wasifu wa kazini?"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"Ungependa kupiga simu ukitumia programu ya kazini?"</string> - <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"Ungependa kubadilisha ili utumie programu ya kazini?"</string> + <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"Ungependa kutumia programu ya kazini?"</string> <string name="miniresolver_call_information" msgid="6739417525304184083">"Shirika lako linakuruhusu upige simu ukitumia programu za kazini pekee"</string> <string name="miniresolver_sms_information" msgid="4311292661329483088">"Shirika lako linakuruhusu utume ujumbe ukitumia programu za kazini pekee"</string> <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Tumia kivinjari cha binafsi"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 146703366385..c6a4530ba469 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்தில் இதை அணுக முடியாது. அதற்குப் பதிலாக உங்கள் மொபைலில் முயன்று பாருங்கள்."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"இந்த ஆப்ஸ் Androidன் பழைய பதிப்பிற்காக உருவாக்கப்பட்டது. இது சரியாகச் செயல்படாமல் போகலாம். மேலும் சமீபத்திய பாதுகாப்பு மற்றும் தனியுரிமை அம்சங்கள் இதில் இல்லை. புதுப்பிப்பு உள்ளதா எனப் பாருங்கள் அல்லது ஆப்ஸ் டெவெலப்பரைத் தொடர்புகொள்ளுங்கள்."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"புதுப்பிப்பு உள்ளதா எனப் பார்"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"இந்த ஆப்ஸ் சமீபத்திய Android பதிப்புடன் இணங்கவில்லை. புதுப்பிப்பு உள்ளதா எனப் பாருங்கள் அல்லது ஆப்ஸ் டெவெலப்பரைத் தொடர்புகொள்ளுங்கள்."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"புதிய செய்திகள் வந்துள்ளன"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"பார்க்க, SMS பயன்பாட்டைத் திறக்கவும்"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"சில செயலுக்கு கட்டுப்பாடு இருக்கலாம்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index f0e9201f1dd4..174429cb573e 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -682,7 +682,7 @@ <string name="face_acquired_too_close" msgid="4453646176196302462">"ఫోన్ను కాస్త దూరంగా జరపండి"</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"ఫోన్ను దగ్గరగా పట్టుకోండి"</string> <string name="face_acquired_too_high" msgid="8278815780046368576">"ఫోన్ను పైకి పట్టుకోండి"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"ఫోన్ను కిందికి దించండి"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"ఫోన్ను కిందికి జరపండి"</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"ఫోన్ను మీ ఎడమ వైపునకు జరపండి"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"ఫోన్ను మీ కుడి వైపునకు జరపండి"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"దయచేసి మీ పరికరం వైపు మరింత నేరుగా చూడండి."</string> @@ -965,7 +965,7 @@ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"అన్లాక్ చేయడానికి లేదా అత్యవసర కాల్ చేయడానికి మెనూ నొక్కండి."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"అన్లాక్ చేయడానికి మెనూ నొక్కండి."</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"అన్లాక్ చేయడానికి నమూనాను గీయండి"</string> - <string name="lockscreen_emergency_call" msgid="7500692654885445299">"అత్యవసరం"</string> + <string name="lockscreen_emergency_call" msgid="7500692654885445299">"ఎమర్జెన్సీ"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"కాల్కు తిరిగి వెళ్లు"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"సరైనది!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"మళ్లీ ట్రై చేయండి"</string> @@ -1698,11 +1698,11 @@ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g>కి మీ పరికరంపై పూర్తి కంట్రోల్ను ఇవ్వాలనుకుంటున్నారా?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"అవసరమైన యాక్సెసిబిలిటీ కోసం యాప్లకు పూర్తి కంట్రోల్ ఇవ్వడం తగిన పనే అయినా, అన్ని యాప్లకు అలా ఇవ్వడం సరికాదు."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"స్క్రీన్ను చూసి, కంట్రోల్ చేయగలగడం"</string> - <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"స్క్రీన్పై ఉండే కంటెంట్ మొత్తాన్ని చదవగలుగుతుంది మరియు ఇతర యాప్లలో కూడా ఈ కంటెంట్ను ప్రదర్శిస్తుంది."</string> + <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"స్క్రీన్పై ఉండే కంటెంట్ మొత్తాన్ని చదవగలుగుతుంది. అంతే కాక, ఇతర యాప్లపై కంటెంట్ను డిస్ప్లే చేస్తుంది."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"చర్యలను చూసి, అమలు చేయగలగడం"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"మీరు ఒక యాప్తో చేసే ఇంటరాక్షన్లను లేదా హార్డ్వేర్ సెన్సార్ను ట్రాక్ చేస్తూ మీ తరఫున యాప్లతో ఇంటరాక్ట్ చేయగలదు."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"అనుమతించండి"</string> - <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"నిరాకరించు"</string> + <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"వద్దు"</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ఫీచర్ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"</string> <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"యాక్సెసిబిలిటీ బటన్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string> <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"వాల్యూమ్ కీ షార్ట్కట్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string> @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"కలర్ కరెక్షన్"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ఎక్స్ట్రా డిమ్"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"వినికిడి పరికరం"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"వినికిడి పరికరాలు"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"వాల్యూమ్ కీలను రిలీజ్ చేయండి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ను ఆన్ చేయడానికి, రెండు వాల్యూమ్ కీలను మళ్లీ 3 సెకన్ల పాటు నొక్కి పట్టుకోండి."</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"మీ <xliff:g id="DEVICE">%1$s</xliff:g>లో దీన్ని యాక్సెస్ చేయడం సాధ్యపడదు. బదులుగా మీ ఫోన్లో ట్రై చేయండి."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"ఈ యాప్ పాత Android వెర్షన్ కోసం రూపొందించబడింది. ఇది సరిగ్గా పని చేయకపోవచ్చు, ఇంకా దీనిలో తాజా సెక్యూరిటీ, గోప్యతా రక్షణలు ఉండకపోవచ్చు. అప్డేట్ కోసం చెక్ చేయండి, లేదా యాప్ డెవలపర్ను సంప్రదించండి."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్డేట్ కోసం చెక్ చేయండి"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"ఈ యాప్ Android ఇటీవలి వెర్షన్కు అనుకూలంగా లేదు. అప్డేట్ కోసం చెక్ చేయండి లేదా యాప్ డెవలపర్ను కాంటాక్ట్ చేయండి."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త మెసేజ్లు ఉన్నాయి"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"చూడటానికి SMS యాప్ను తెరవండి"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"కొంత ఫంక్షనాలిటీ పరిమితం కావచ్చు"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index bfe85bf2b7a5..e5f76431d357 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"เข้าถึงการตั้งค่านี้ใน <xliff:g id="DEVICE">%1$s</xliff:g> ของคุณไม่ได้ โปรดลองเข้าถึงในโทรศัพท์แทน"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"แอปนี้สร้างมาเพื่อ Android เวอร์ชันเก่า ซึ่งอาจทำงานไม่ถูกต้องและไม่มีการคุ้มครองความปลอดภัยและความเป็นส่วนตัวเวอร์ชันล่าสุด ตรวจหาอัปเดตหรือติดต่อนักพัฒนาแอป"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ตรวจสอบอัปเดต"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"แอปนี้ไม่สามารถใช้งานร่วมกับ Android เวอร์ชันล่าสุด ตรวจหาอัปเดตหรือติดต่อนักพัฒนาแอป"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"คุณมีข้อความใหม่"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"เปิดแอป SMS เพื่อดู"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"อาจมีข้อจำกัดในบางฟังก์ชัน"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 0a1b9a09eda7..1371de6143e9 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Hindi ito maa-access sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>. Subukan na lang sa iyong telepono."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ginawa ang app na ito para sa mas lumang bersyon ng Android. Baka hindi ito gumana nang maayos at wala itong pinakabagong proteksyon sa seguridad at privacy. Tingnan kung may update, o makipag-ugnayan sa developer ng app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tingnan kung may update"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Hindi compatible ang app sa pinakabagong bersyon ng Android. Tingnan kung may update o makipag-ugnayan sa developer ng app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Mayroon kang mga bagong mensahe"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Buksan ang SMS app upang tingnan"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Puwedeng limitado ang ilang function"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index f5228565807d..e73c55d9964e 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Bu uygulamaya <xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan erişilemiyor. Bunun yerine telefonunuzu kullanmayı deneyin."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Bu uygulama Android\'in daha eski bir sürümü için geliştirildi. Bu nedenle, düzgün çalışmayabilir ve son güvenlik ile gizlilik korumalarını içermemektedir. Güncelleme olup olmadığını kontrol edin veya uygulamanın geliştiricisiyle iletişime geçin."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncellemeleri denetle"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Bu uygulama Android\'in son sürümüyle uyumlu değil. Güncelleme olup olmadığını kontrol edin veya uygulamanın geliştiricisiyle iletişime geçin."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Görüntülemek için SMS uygulamasını açın"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Bazı işlevler sınırlanabilir"</string> @@ -2165,7 +2166,7 @@ <string name="miniresolver_open_work" msgid="6286176185835401931">"İş uygulaması (<xliff:g id="APP">%s</xliff:g>) açılsın mı?"</string> <string name="miniresolver_open_in_personal" msgid="807427577794490375">"Kişisel uygulamada (<xliff:g id="APP">%s</xliff:g>) açılsın mı?"</string> <string name="miniresolver_open_in_work" msgid="941341494673509916">"İş uygulamasında (<xliff:g id="APP">%s</xliff:g>) açılsın mı?"</string> - <string name="miniresolver_call_in_work" msgid="528779988307529039">"İş uygulamasından telefon edilsin mi?"</string> + <string name="miniresolver_call_in_work" msgid="528779988307529039">"İş uygulamasından aransın mı?"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"İş uygulamasına geçilsin mi?"</string> <string name="miniresolver_call_information" msgid="6739417525304184083">"Kuruluşunuz yalnızca iş uygulamalarından telefon etmenize izin veriyor"</string> <string name="miniresolver_sms_information" msgid="4311292661329483088">"Kuruluşunuz yalnızca iş uygulamalarından mesaj göndermenize izin veriyor"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index b5d4bb7cc111..a8345eda3c5f 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1977,6 +1977,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Немає доступу на вашому пристрої (<xliff:g id="DEVICE">%1$s</xliff:g>). Спробуйте натомість скористатися телефоном."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Цей додаток створено для старішої версії ОС Android. Він може не працювати належним чином і не містить найновіших засобів захисту конфіденційності та безпеки. Перевірте наявність оновлень або зв’яжіться з розробником додатка."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Шукати оновлення"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Цей додаток несумісний з останньою версією ОС Android. Перевірте наявність оновлень або зв’яжіться з розробником додатка."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"У вас є нові повідомлення"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Щоб переглянути, відкрийте додаток для SMS"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Деякі функції може бути обмежено"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index ea41d9dd10eb..a5658a8ce786 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> پر اس تک رسائی حاصل نہیں ہو سکتی۔ اس کے بجائے اپنے فون پر کوشش کریں۔"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"یہ ایپ Android کے پرانے ورژن کے لیے بنائی گئی تھی۔ ہو سکتا ہے یہ ٹھیک سے کام نہ کرے اور اس میں تازہ ترین سیکیورٹی اور رازداری کے تحفظات شامل نہ ہوں۔ اپ ڈیٹ کے لیے چیک کریں یا ایپ کے ڈویلپر سے رابطہ کریں۔"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"اپ ڈیٹ چیک کریں"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"یہ ایپ Android کے تازہ ترین ورژن کے ساتھ مطابقت پذیر نہیں ہے۔ اپ ڈیٹ کے لیے چیک کریں یا ایپ کے ڈویلپر سے رابطہ کریں۔"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"آپ کے پاس نئے پیغامات ہیں"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"دیکھنے کیلئے SMS ایپ کھولیں"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"کچھ فعالیت محدود ہو سکتی ہے"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index 154402e0e4ac..0e749faeb8aa 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Bu <xliff:g id="DEVICE">%1$s</xliff:g> qurilmangizda ochilmaydi. Telefon orqali urininb koʻring."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Bu ilova Androidning eskiroq versiyasiga moʻljallab ishlab chiqilgan. Xatosiz ishlashi mumkin, lekin xavfsizlik va maxfiylik himoyasiga oid oxirgi yangilanishlarini olmaydi. Yangilanish borligini tekshiring yoki ilova ishlab chiquvchisiga murojaat qiling."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Yangilanish borligini tekshirish"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Bu ilova Androidning oxirgi versiyasiga mos emas. Yangilanish borligini tekshiring yoki ilova ishlab chiquvchisiga murojaat qiling."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Sizga yangi SMS keldi"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Ko‘rish uchun SMS ilovasini oching"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Ayrim funksiyalar ishlamasligi mumkin"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 628f02abfeca..ac4358ba717e 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Hiện tại, bạn không thể truy cập vào ứng dụng này trên <xliff:g id="DEVICE">%1$s</xliff:g>. Hãy thử trên điện thoại."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Ứng dụng này được xây dựng cho một phiên bản Android cũ. Ứng dụng này có thể không hoạt động đúng cách và không có các biện pháp bảo vệ mới nhất về bảo mật và quyền riêng tư. Hãy kiểm tra để tìm bản cập nhật hoặc liên hệ với nhà phát triển của ứng dụng."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kiểm tra để tìm bản cập nhật"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Ứng dụng này không tương thích với phiên bản Android mới nhất. Hãy kiểm tra để tìm bản cập nhật hoặc liên hệ với nhà phát triển của ứng dụng."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Bạn có tin nhắn mới"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Mở ứng dụng SMS để xem"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Một số chức năng có thể bị hạn chế"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 3272694f693f..e938fbfa1a66 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -702,7 +702,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"无法创建您的脸部模型,请重试。"</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"检测到墨镜,您的脸部必须完全可见。"</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"检测到脸部有遮挡物,您的脸部必须完全可见。"</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"检测到脸部存在遮挡,请露出整张脸。"</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"无法验证人脸。硬件无法使用。"</string> @@ -1715,7 +1715,7 @@ <string name="color_correction_feature_name" msgid="7975133554160979214">"色彩校正"</string> <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"单手模式"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"极暗"</string> - <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助听设备"</string> + <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助听装置"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string> <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string> <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"松开音量键。如要启用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,请再次同时按住两个音量键 3 秒。"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"无法在您的<xliff:g id="DEVICE">%1$s</xliff:g>上访问此设置,您可以尝试在手机上访问。"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"此应用专为旧版 Android 系统打造。它可能无法正常运行,也不包含最新的安全和隐私保护功能。请检查是否有更新,或与应用开发者联系。"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"检查更新"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"此应用与最新版 Android 不兼容。请检查是否有更新,或与应用开发者联系。"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"您有新消息"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"打开短信应用查看"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"部分功能可能会受到限制"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 997f963ce802..b0f4c04d9603 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1695,7 +1695,7 @@ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"開啟"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"關閉"</string> - <string name="accessibility_enable_service_title" msgid="3931558336268541484">"要授予「<xliff:g id="SERVICE">%1$s</xliff:g>」裝置的完整控制權?"</string> + <string name="accessibility_enable_service_title" msgid="3931558336268541484">"要授予「<xliff:g id="SERVICE">%1$s</xliff:g>」裝置的完整控制權嗎?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"對於為你提供無障礙功能的應用程式,你可授予完整控制權,但大部分應用程式都不應獲授予此權限。"</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"查看和控制螢幕"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"這項功能可以讀出螢幕上的所有內容,並透過其他應用程式顯示內容。"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取此應用程式,請改用手機。"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"此應用程式專為舊版 Android 而設。因此可能無法正常運作,且不提供最新的安全性和私隱保護。請檢查是否有更新版本,或聯絡應用程式開發人員。"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"檢查更新"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"此應用程式不兼容最新 Android 版本。請檢查是否有更新版本,或聯絡應用程式開發人員。"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"你有新的訊息"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"開啟短訊應用程式查看內容"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"部分功能可能會受到限制"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index ea04067f4e86..14ced72ebf01 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -461,10 +461,10 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"允許應用程式修改平板電腦的通話記錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改你的通話記錄。"</string> <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"允許應用程式修改 Android TV 裝置的通話記錄,包括來電和撥出電話相關資料。惡意應用程式可能會藉此清除或修改你的通話記錄。"</string> <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"允許應用程式修改手機的通話記錄,包括來電和已撥電話相關資料。請注意,惡意應用程式可能濫用此功能刪除或修改你的通話記錄。"</string> - <string name="permlab_bodySensors" msgid="662918578601619569">"在使用期間可存取人體感應器資料,例如心跳速率"</string> - <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"允許應用程式在使用期間存取人體感應器資料,例如心跳速率、體溫和血氧比例。"</string> - <string name="permlab_bodySensors_background" msgid="4912560779957760446">"在背景執行時可存取人體感應器資料,例如心跳速率"</string> - <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"允許應用程式在背景執行時存取人體感應器資料,例如心跳速率、體溫和血氧比例。"</string> + <string name="permlab_bodySensors" msgid="662918578601619569">"在使用期間可存取人體感應器資料,例如心率"</string> + <string name="permdesc_bodySensors" product="default" msgid="7652650410295512140">"允許應用程式在使用期間存取人體感應器資料,例如心率、體溫和血氧比例。"</string> + <string name="permlab_bodySensors_background" msgid="4912560779957760446">"在背景執行時可存取人體感應器資料,例如心率"</string> + <string name="permdesc_bodySensors_background" product="default" msgid="8870726027557749417">"允許應用程式在背景執行時存取人體感應器資料,例如心率、體溫和血氧比例。"</string> <string name="permlab_readCalendar" msgid="6408654259475396200">"讀取日曆活動和詳細資訊"</string> <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"這個應用程式可讀取所有儲存在平板電腦上的日曆活動資訊,以及共用或儲存日曆資料。"</string> <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"這個應用程式可讀取所有儲存在 Android TV 裝置上的日曆活動,以及共用或儲存日曆資料。"</string> @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"無法在 <xliff:g id="DEVICE">%1$s</xliff:g> 上存取這個應用程式,請改用手機。"</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"這個應用程式是專為舊版 Android 所打造,因此可能無法正常運作,且不提供最新的安全性和隱私保護服務。請檢查是否有更新版本,或與應用程式的開發人員聯絡。"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"檢查更新"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"這個應用程式與最新版的 Android 不相容。請檢查是否有可用的更新,或與應用程式發人員聯絡。"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"你有新訊息"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"開啟簡訊應用程式來查看內容"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"部分功能可能遭到鎖定"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index b2ef74a5b5d6..e432c69d36de 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1975,6 +1975,7 @@ <string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Lokhu akukwazi ukufinyelelwa ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho. Zama efonini yakho kunalokho."</string> <string name="deprecated_target_sdk_message" msgid="5246906284426844596">"Le app yakhelwe uhlobo lwakudala le-Android. Ingase ingasebenzi kahle futhi ayinakho ukuvikeleka kwakamuva nokuvikelwa kobumfihlo. Hlola isibuyekezo, noma uthinte unjiniyela we-app."</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Hlola izibuyekezo"</string> + <string name="deprecated_abi_message" msgid="6820548011196218091">"Le app ayihambisani nohlobo lwakamuva lwe-Android. Hlola isibuyekezo, noma uthinte unjiniyela we-app."</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"Unemilayezo emisha"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"Vula uhlelo lokusebenza lwe-SMS ukuze ubuke"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"Okunye ukusebenza kungakhawulelwe"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 00f8db0d9264..ee8c0f83304e 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1817,16 +1817,6 @@ specified --> <string name="default_wallpaper_component" translatable="false">@null</string> - <!-- CMF colors to default wallpaper component map, the component with color matching the device - color will be the cmf default wallpapers. The default wallpaper will be default wallpaper - component if not specified. - - E.g. for SLV color, and com.android.example/com.android.example.SlVDefaultWallpaper - <item>SLV,com.android.example/com.android.example.SlVDefaultWallpaper</item> --> - <string-array name="cmf_default_wallpaper_component" translatable="false"> - <!-- Add packages here --> - </string-array> - <!-- By default a product has no distinct default lock wallpaper --> <item name="default_lock_wallpaper" type="drawable">@null</item> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index fb3acbe79114..a5b2b853fddd 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5035,8 +5035,8 @@ <string name="display_rotation_camera_compat_toast_after_rotation">Rotate for a better view</string> <!-- Text on a toast shown when a camera view is started within the app that may not be able - to display the camera preview correctly while in split screen. [CHAR LIMIT=NONE] --> - <string name="display_rotation_camera_compat_toast_in_split_screen">Exit split screen for a better view</string> + to display the camera preview correctly while in multi-window. [CHAR LIMIT=NONE] --> + <string name="display_rotation_camera_compat_toast_in_multi_window">Open <xliff:g id="name" example="MyApp">%s</xliff:g> in full screen for a better view</string> <!-- Label for button to confirm chosen date or time [CHAR LIMIT=30] --> <string name="done_label">Done</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6ab671a70c3c..dc4eafd2e00e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2116,7 +2116,6 @@ <java-symbol type="string" name="data_usage_rapid_body" /> <java-symbol type="string" name="data_usage_rapid_app_body" /> <java-symbol type="string" name="default_wallpaper_component" /> - <java-symbol type="array" name="cmf_default_wallpaper_component" /> <java-symbol type="string" name="device_storage_monitor_notification_channel" /> <java-symbol type="string" name="dlg_ok" /> <java-symbol type="string" name="dump_heap_notification" /> @@ -2533,7 +2532,7 @@ <java-symbol type="string" name="zen_mode_default_events_name" /> <java-symbol type="string" name="zen_mode_default_every_night_name" /> <java-symbol type="string" name="display_rotation_camera_compat_toast_after_rotation" /> - <java-symbol type="string" name="display_rotation_camera_compat_toast_in_split_screen" /> + <java-symbol type="string" name="display_rotation_camera_compat_toast_in_multi_window" /> <java-symbol type="array" name="config_system_condition_providers" /> <java-symbol type="string" name="muted_by" /> <java-symbol type="string" name="zen_mode_alarm" /> diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index a7538701807a..0525443ecf82 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import android.util.Property; import android.view.View; import androidx.test.annotation.UiThreadTest; @@ -576,6 +577,42 @@ public class AnimatorSetActivityTest { }); } + @Test + public void testInitializeWithoutReadingValues() throws Throwable { + // Some consumers crash while reading values before the animator starts + Property<int[], Integer> property = new Property<>(Integer.class, "firstValue") { + @Override + public Integer get(int[] target) { + throw new IllegalStateException("Shouldn't be called"); + } + + @Override + public void set(int[] target, Integer value) { + target[0] = value; + } + }; + + int[] target1 = new int[1]; + int[] target2 = new int[1]; + int[] target3 = new int[1]; + ObjectAnimator animator1 = ObjectAnimator.ofInt(target1, property, 0, 100); + ObjectAnimator animator2 = ObjectAnimator.ofInt(target2, property, 0, 100); + ObjectAnimator animator3 = ObjectAnimator.ofInt(target3, property, 0, 100); + AnimatorSet set = new AnimatorSet(); + set.playSequentially(animator1, animator2, animator3); + + mActivityRule.runOnUiThread(() -> { + set.setCurrentPlayTime(900); + assertEquals(100, target1[0]); + assertEquals(100, target2[0]); + assertEquals(100, target3[0]); + set.setCurrentPlayTime(0); + assertEquals(0, target1[0]); + assertEquals(0, target2[0]); + assertEquals(0, target3[0]); + }); + } + /** * Check that the animator list contains exactly the given animators and nothing else. */ diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java index 4f8b85554f5c..b5f18c26dc97 100644 --- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java +++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java @@ -45,36 +45,40 @@ public class ConfigurationBoundResourceCacheTest @SmallTest public void testGetEmpty() { final Resources res = getActivity().getResources(); - assertNull(mCache.getInstance(-1, res, null)); + assertNull(mCache.getInstance(-1, res, null).getValue()); } @SmallTest public void testSetGet() { - mCache.put(1, null, new DummyFloatConstantState(5f)); + mCache.put(1, null, new DummyFloatConstantState(5f), + ThemedResourceCache.UNDEFINED_GENERATION); final Resources res = getActivity().getResources(); - assertEquals(5f, mCache.getInstance(1, res, null)); - assertNotSame(5f, mCache.getInstance(1, res, null)); - assertEquals(null, mCache.getInstance(1, res, getActivity().getTheme())); + assertEquals(5f, mCache.getInstance(1, res, null).getValue()); + assertNotSame(5f, mCache.getInstance(1, res, null).getValue()); + assertEquals(false, mCache.getInstance(1, res, getActivity().getTheme()).hasValue()); } @SmallTest public void testSetGetThemed() { - mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f)); + mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f), + ThemedResourceCache.UNDEFINED_GENERATION); final Resources res = getActivity().getResources(); - assertEquals(null, mCache.getInstance(1, res, null)); - assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme())); - assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme())); + assertEquals(false, mCache.getInstance(1, res, null).hasValue()); + assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); + assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); } @SmallTest public void testMultiThreadPutGet() { - mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f)); - mCache.put(1, null, new DummyFloatConstantState(10f)); + mCache.put(1, getActivity().getTheme(), new DummyFloatConstantState(5f), + ThemedResourceCache.UNDEFINED_GENERATION); + mCache.put(1, null, new DummyFloatConstantState(10f), + ThemedResourceCache.UNDEFINED_GENERATION); final Resources res = getActivity().getResources(); - assertEquals(10f, mCache.getInstance(1, res, null)); - assertNotSame(10f, mCache.getInstance(1, res, null)); - assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme())); - assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme())); + assertEquals(10f, mCache.getInstance(1, res, null).getValue()); + assertNotSame(10f, mCache.getInstance(1, res, null).getValue()); + assertEquals(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); + assertNotSame(5f, mCache.getInstance(1, res, getActivity().getTheme()).getValue()); } @SmallTest @@ -86,16 +90,17 @@ public class ConfigurationBoundResourceCacheTest res.getValue(R.dimen.resource_cache_test_generic, staticValue, true); float staticDim = TypedValue.complexToDimension(staticValue.data, res.getDisplayMetrics()); mCache.put(key, getActivity().getTheme(), - new DummyFloatConstantState(staticDim, staticValue.changingConfigurations)); + new DummyFloatConstantState(staticDim, staticValue.changingConfigurations), + ThemedResourceCache.UNDEFINED_GENERATION); final Configuration cfg = res.getConfiguration(); Configuration newCnf = new Configuration(cfg); newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; int changes = calcConfigChanges(res, newCnf); - assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme())); + assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()).getValue()); mCache.onConfigurationChange(changes); - assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme())); + assertEquals(staticDim, mCache.getInstance(key, res, getActivity().getTheme()).getValue()); } @SmallTest @@ -108,7 +113,8 @@ public class ConfigurationBoundResourceCacheTest float changingDim = TypedValue.complexToDimension(changingValue.data, res.getDisplayMetrics()); mCache.put(key, getActivity().getTheme(), - new DummyFloatConstantState(changingDim, changingValue.changingConfigurations)); + new DummyFloatConstantState(changingDim, changingValue.changingConfigurations), + ThemedResourceCache.UNDEFINED_GENERATION); final Configuration cfg = res.getConfiguration(); Configuration newCnf = new Configuration(cfg); @@ -116,9 +122,10 @@ public class ConfigurationBoundResourceCacheTest Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; int changes = calcConfigChanges(res, newCnf); - assertEquals(changingDim, mCache.getInstance(key, res, getActivity().getTheme())); + assertEquals(changingDim, + mCache.getInstance(key, res, getActivity().getTheme()).getValue()); mCache.onConfigurationChange(changes); - assertNull(mCache.get(key, getActivity().getTheme())); + assertNull(mCache.get(key, getActivity().getTheme()).getValue()); } @SmallTest @@ -133,9 +140,11 @@ public class ConfigurationBoundResourceCacheTest float changingDim = TypedValue.complexToDimension(changingValue.data, res.getDisplayMetrics()); mCache.put(R.dimen.resource_cache_test_generic, getActivity().getTheme(), - new DummyFloatConstantState(staticDim, staticValue.changingConfigurations)); + new DummyFloatConstantState(staticDim, staticValue.changingConfigurations), + ThemedResourceCache.UNDEFINED_GENERATION); mCache.put(R.dimen.resource_cache_test_orientation_dependent, getActivity().getTheme(), - new DummyFloatConstantState(changingDim, changingValue.changingConfigurations)); + new DummyFloatConstantState(changingDim, changingValue.changingConfigurations), + ThemedResourceCache.UNDEFINED_GENERATION); final Configuration cfg = res.getConfiguration(); Configuration newCnf = new Configuration(cfg); newCnf.orientation = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE ? @@ -143,15 +152,15 @@ public class ConfigurationBoundResourceCacheTest : Configuration.ORIENTATION_LANDSCAPE; int changes = calcConfigChanges(res, newCnf); assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res, - getActivity().getTheme())); + getActivity().getTheme()).getValue()); assertEquals(changingDim, mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - getActivity().getTheme())); + getActivity().getTheme()).getValue()); mCache.onConfigurationChange(changes); assertEquals(staticDim, mCache.getInstance(R.dimen.resource_cache_test_generic, res, - getActivity().getTheme())); + getActivity().getTheme()).getValue()); assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - getActivity().getTheme())); + getActivity().getTheme()).getValue()); } @SmallTest @@ -173,10 +182,12 @@ public class ConfigurationBoundResourceCacheTest res.getDisplayMetrics()); final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; mCache.put(R.dimen.resource_cache_test_generic, theme, - new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations)); + new DummyFloatConstantState(staticDim, staticValues[i].changingConfigurations), + ThemedResourceCache.UNDEFINED_GENERATION); mCache.put(R.dimen.resource_cache_test_orientation_dependent, theme, new DummyFloatConstantState(changingDim, - changingValues[i].changingConfigurations)); + changingValues[i].changingConfigurations), + ThemedResourceCache.UNDEFINED_GENERATION); } final Configuration cfg = res.getConfiguration(); Configuration newCnf = new Configuration(cfg); @@ -187,18 +198,18 @@ public class ConfigurationBoundResourceCacheTest for (int i = 0; i < 2; i++) { final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; assertEquals(staticDim, - mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme)); + mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme).getValue()); assertEquals(changingDim, mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - theme)); + theme).getValue()); } mCache.onConfigurationChange(changes); for (int i = 0; i < 2; i++) { final Resources.Theme theme = i == 0 ? getActivity().getTheme() : null; assertEquals(staticDim, - mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme)); + mCache.getInstance(R.dimen.resource_cache_test_generic, res, theme).getValue()); assertNull(mCache.getInstance(R.dimen.resource_cache_test_orientation_dependent, res, - theme)); + theme).getValue()); } } diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..e7b1110f898a 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + var p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + var p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 2ef2d3a968e0..a6e74d0d6b94 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -340,4 +341,42 @@ public class WindowOnBackInvokedDispatcherTest { verify(mCallback1).onBackCancelled(); verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), isNull()); } + + @Test + public void onBackInvoked_calledAfterOnBackStarted() throws RemoteException { + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1); + OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo(); + + callbackInfo.getCallback().onBackStarted(mBackEvent); + + waitForIdle(); + verify(mCallback1).onBackStarted(any(BackEvent.class)); + + callbackInfo.getCallback().onBackInvoked(); + + waitForIdle(); + verify(mCallback1).onBackInvoked(); + verify(mCallback1, never()).onBackCancelled(); + } + + @Test + public void onDetachFromWindow_cancelCallbackAndIgnoreOnBackInvoked() throws RemoteException { + mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1); + + OnBackInvokedCallbackInfo callbackInfo = assertSetCallbackInfo(); + + callbackInfo.getCallback().onBackStarted(mBackEvent); + + waitForIdle(); + verify(mCallback1).onBackStarted(any(BackEvent.class)); + + // This should trigger mCallback1.onBackCancelled() + mDispatcher.detachFromWindow(); + // This should be ignored by mCallback1 + callbackInfo.getCallback().onBackInvoked(); + + waitForIdle(); + verify(mCallback1, never()).onBackInvoked(); + verify(mCallback1).onBackCancelled(); + } } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index e4defcfa359f..93e44f1d2f87 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1813,6 +1813,12 @@ "group": "WM_DEBUG_KEEP_SCREEN_ON", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "-479665533": { + "message": "DisplayRotationCompatPolicy: Multi-window toast not shown as package '%s' cannot be found.", + "level": "ERROR", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java" + }, "-464564167": { "message": "Current transition prevents automatic focus change", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 2307d6080f9f..b9d3756ac6d2 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -997,12 +997,63 @@ public final class Bitmap implements Parcelable { canvas.concat(m); canvas.drawBitmap(source, srcR, dstR, paint); canvas.setBitmap(null); + + // If the source has a gainmap, apply the same set of transformations to the gainmap + // and set it on the output + if (source.hasGainmap()) { + Bitmap newMapContents = transformGainmap(source, m, neww, newh, paint, srcR, dstR, + deviceR); + if (newMapContents != null) { + bitmap.setGainmap(new Gainmap(source.getGainmap(), newMapContents)); + } + } + if (isHardware) { return bitmap.copy(Config.HARDWARE, false); } return bitmap; } + private static Bitmap transformGainmap(Bitmap source, Matrix m, int neww, int newh, Paint paint, + Rect srcR, RectF dstR, RectF deviceR) { + Canvas canvas; + Bitmap sourceGainmap = source.getGainmap().getGainmapContents(); + // Gainmaps can be scaled relative to the base image (eg, 1/4th res) + // Preserve that relative scaling between the base & gainmap in the output + float scaleX = (sourceGainmap.getWidth() / (float) source.getWidth()); + float scaleY = (sourceGainmap.getHeight() / (float) source.getHeight()); + int mapw = Math.round(neww * scaleX); + int maph = Math.round(newh * scaleY); + + if (mapw == 0 || maph == 0) { + // The gainmap has been scaled away entirely, drop it + return null; + } + + // Scale the computed `srcR` used for rendering the source bitmap to the destination + // to be in gainmap dimensions + Rect gSrcR = new Rect((int) (srcR.left * scaleX), + (int) (srcR.top * scaleY), (int) (srcR.right * scaleX), + (int) (srcR.bottom * scaleY)); + + // Note: createBitmap isn't used as that requires a non-null colorspace, however + // gainmaps don't have a colorspace. So use `nativeCreate` directly to bypass + // that colorspace enforcement requirement (#getColorSpace() allows a null return) + Bitmap newMapContents = nativeCreate(null, 0, mapw, mapw, maph, + sourceGainmap.getConfig().nativeInt, true, 0); + newMapContents.eraseColor(0); + canvas = new Canvas(newMapContents); + // Scale the translate & matrix to be in gainmap-relative dimensions + canvas.scale(scaleX, scaleY); + canvas.translate(-deviceR.left, -deviceR.top); + canvas.concat(m); + canvas.drawBitmap(sourceGainmap, gSrcR, dstR, paint); + canvas.setBitmap(null); + // Create a new gainmap using a copy of the metadata information from the source but + // with the transformed bitmap created above + return newMapContents; + } + /** * Returns a mutable bitmap with the specified width and height. Its * initial density is as per {@link #getDensity}. The newly created diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java index 9ac84a6159da..f639521ff250 100644 --- a/graphics/java/android/graphics/Gainmap.java +++ b/graphics/java/android/graphics/Gainmap.java @@ -122,6 +122,16 @@ public final class Gainmap implements Parcelable { } /** + * Creates a new gainmap using the provided gainmap as the metadata source and the provided + * bitmap as the replacement for the gainmapContents + * TODO: Make public, it's useful + * @hide + */ + public Gainmap(@NonNull Gainmap gainmap, @NonNull Bitmap gainmapContents) { + this(gainmapContents, nCreateCopy(gainmap.mNativePtr)); + } + + /** * @return Returns the image data of the gainmap represented as a Bitmap. This is represented * as a Bitmap for broad API compatibility, however certain aspects of the Bitmap are ignored * such as {@link Bitmap#getColorSpace()} or {@link Bitmap#getGainmap()} as they are not @@ -325,6 +335,7 @@ public final class Gainmap implements Parcelable { private static native long nGetFinalizer(); private static native long nCreateEmpty(); + private static native long nCreateCopy(long source); private static native void nSetBitmap(long ptr, Bitmap bitmap); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java index 7571e44a7713..d12989187281 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java @@ -24,6 +24,7 @@ import android.security.KeyStoreException; import android.security.KeyStoreOperation; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyStoreCryptoOperation; +import android.system.keystore2.Authorization; import libcore.util.EmptyArray; @@ -119,6 +120,14 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor mCipher = null; } + private Authorization[] getKeyCharacteristics(Key key) { + if (!(key instanceof AndroidKeyStoreKey)) { + return new Authorization[] {}; + } + + return ((AndroidKeyStoreKey) key).getAuthorizations(); + } + @Override protected final void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { @@ -173,7 +182,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor init(opmode, key, random); initAlgorithmSpecificParameters(); try { - ensureKeystoreOperationInitialized(); + ensureKeystoreOperationInitialized(getKeyCharacteristics(key)); } catch (InvalidAlgorithmParameterException e) { throw new InvalidKeyException(e); } @@ -206,7 +215,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor try { init(opmode, key, random); initAlgorithmSpecificParameters(params); - ensureKeystoreOperationInitialized(); + ensureKeystoreOperationInitialized(getKeyCharacteristics(key)); success = true; } finally { if (!success) { @@ -236,7 +245,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor try { init(opmode, key, random); initAlgorithmSpecificParameters(params); - ensureKeystoreOperationInitialized(); + ensureKeystoreOperationInitialized(getKeyCharacteristics(key)); success = true; } finally { if (!success) { @@ -310,7 +319,8 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor mCachedException = null; } - private void ensureKeystoreOperationInitialized() throws InvalidKeyException, + private void ensureKeystoreOperationInitialized(Authorization[] keyCharacteristics) + throws InvalidKeyException, InvalidAlgorithmParameterException { if (mMainDataStreamer != null) { return; @@ -323,7 +333,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor } List<KeyParameter> parameters = new ArrayList<>(); - addAlgorithmSpecificParametersToBegin(parameters); + addAlgorithmSpecificParametersToBegin(parameters, keyCharacteristics); int purpose; if (mKeymasterPurposeOverride != -1) { @@ -404,7 +414,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor return null; } try { - ensureKeystoreOperationInitialized(); + ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey)); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { mCachedException = e; return null; @@ -520,7 +530,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor } try { - ensureKeystoreOperationInitialized(); + ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey)); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { mCachedException = e; return; @@ -597,7 +607,7 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor } try { - ensureKeystoreOperationInitialized(); + ensureKeystoreOperationInitialized(getKeyCharacteristics(mKey)); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e); } @@ -1012,6 +1022,22 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor @NonNull List<KeyParameter> parameters); /** + * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation, + * including the key characteristics. This is useful in case the parameters to {@code begin} + * depend on how the key was generated. + * The default implementation provided here simply ignores these key characteristics because + * they are not be needed for most engines. + * + * @param parameters keystore/keymaster arguments to be populated with algorithm-specific + * parameters. + * @param keyCharacteristics The key's characteristics. + */ + protected void addAlgorithmSpecificParametersToBegin( + @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) { + addAlgorithmSpecificParametersToBegin(parameters); + } + + /** * Invoked to obtain algorithm-specific parameters from the result of the KeyStore's * {@code begin} operation. * diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java index e9b66aafc262..3bb2564807b6 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java @@ -288,16 +288,34 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase } } + private static boolean isMgfDigestTagPresentInKeyProperties( + Authorization[] keyCharacteristics) { + for (Authorization authorization : keyCharacteristics) { + if (authorization.keyParameter.tag == KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST) { + return true; + } + } + + return false; + } + @Override protected final void addAlgorithmSpecificParametersToBegin( - @NonNull List<KeyParameter> parameters) { - super.addAlgorithmSpecificParametersToBegin(parameters); + @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) { + super.addAlgorithmSpecificParametersToBegin(parameters, keyCharacteristics); parameters.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest )); - parameters.add(KeyStore2ParameterUtils.makeEnum( - KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest - )); + // Only add the KM_TAG_RSA_OAEP_MGF_DIGEST tag to begin() if the MGF Digest is + // present in the key properties. Keys generated prior to Android 14 did not have + // this tag (Keystore didn't add it) so specifying any MGF digest tag would cause + // a begin() operation (on an Android 14 device) to fail (with a key that was generated + // on Android 13 or below). + if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics)) { + parameters.add(KeyStore2ParameterUtils.makeEnum( + KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest + )); + } } @Override diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 9434d48286b9..8f2f6d8e882f 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -54,7 +54,7 @@ <string name="accessibility_split_top" msgid="2789329702027147146">"上に分割"</string> <string name="accessibility_split_bottom" msgid="8694551025220868191">"下に分割"</string> <string name="one_handed_tutorial_title" msgid="4583241688067426350">"片手モードの使用"</string> - <string name="one_handed_tutorial_description" msgid="3486582858591353067">"終了するには、画面を下から上にスワイプするか、アプリの任意の場所をタップします"</string> + <string name="one_handed_tutorial_description" msgid="3486582858591353067">"終了するには、画面を下から上にスワイプするか、アプリの上側の任意の場所をタップします"</string> <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"片手モードを開始します"</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"片手モードを終了します"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"<xliff:g id="APP_NAME">%1$s</xliff:g> のバブルの設定"</string> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 8635c56b7bc6..d902fd49ba60 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -93,62 +93,34 @@ <style name="RestartDialogTitleText"> <item name="android:textSize">24sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Headline - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamilyMedium - </item> + <item name="android:lineSpacingExtra">8sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> </style> - <style name="RestartDialogBodyText"> + <style name="RestartDialogBodyStyle"> <item name="android:textSize">14sp</item> + <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> + </style> + + <style name="RestartDialogBodyText" parent="RestartDialogBodyStyle"> <item name="android:letterSpacing">0.02</item> <item name="android:textColor">?android:attr/textColorSecondary</item> - <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> + <item name="android:lineSpacingExtra">6sp</item> </style> - <style name="RestartDialogCheckboxText"> - <item name="android:textSize">16sp</item> + <style name="RestartDialogCheckboxText" parent="RestartDialogBodyStyle"> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:lineSpacingExtra">4sp</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Headline - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamilyMedium - </item> + <item name="android:lineSpacingExtra">6sp</item> </style> - <style name="RestartDialogDismissButton"> + <style name="RestartDialogDismissButton" parent="RestartDialogBodyStyle"> <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimary</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> </style> - <style name="RestartDialogConfirmButton"> + <style name="RestartDialogConfirmButton" parent="RestartDialogBodyStyle"> <item name="android:lineSpacingExtra">2sp</item> - <item name="android:textSize">14sp</item> <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> - <item name="android:textAppearance"> - @*android:style/TextAppearance.DeviceDefault.Body2 - </item> - <item name="android:fontFamily"> - @*android:string/config_bodyFontFamily - </item> </style> <style name="ReachabilityEduHandLayout" parent="Theme.AppCompat.Light"> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java index 7b37d5947f17..57d374b2b8f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java @@ -20,6 +20,8 @@ import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; import static android.window.TransitionInfo.FLAG_FILLS_TASK; import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; +import static com.android.wm.shell.transition.DefaultTransitionHandler.isSupportedOverrideAnimation; + import static java.util.Objects.requireNonNull; import android.content.Context; @@ -113,8 +115,11 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle return false; } final TransitionInfo.AnimationOptions options = info.getAnimationOptions(); - if (options != null && options.getType() == ANIM_SCENE_TRANSITION) { - // Scene-transition will be handled by app side. + if (options != null + // Scene-transition will be handled by app side. + && (options.getType() == ANIM_SCENE_TRANSITION + // Use default transition handler to animate override animation. + || isSupportedOverrideAnimation(options))) { return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index 9fcd207dc370..e1a3f3a1ac5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -55,6 +55,7 @@ import android.util.FloatProperty; import android.util.IntProperty; import android.util.Log; import android.util.TypedValue; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -459,7 +460,9 @@ public class BubbleExpandedView extends LinearLayout { if (mManageButton != null) { int visibility = mManageButton.getVisibility(); removeView(mManageButton); - mManageButton = (AlphaOptimizedButton) LayoutInflater.from(getContext()).inflate( + ContextThemeWrapper ctw = new ContextThemeWrapper(getContext(), + com.android.internal.R.style.Theme_DeviceDefault_DayNight); + mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate( R.layout.bubble_manage_button, this /* parent */, false /* attach */); addView(mManageButton); mManageButton.setVisibility(visibility); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java index d6e1a82a68ff..1959eb03a6b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java @@ -300,9 +300,12 @@ public class DisplayLayout { return mAllowSeamlessRotationDespiteNavBarMoving; } - /** @return whether the navigation bar will change sides during rotation. */ + /** + * Returns {@code true} if the navigation bar will change sides during rotation and the display + * is not square. + */ public boolean navigationBarCanMove() { - return mNavigationBarCanMove; + return mNavigationBarCanMove && mWidth != mHeight; } /** @return the rotation that would make the physical display "upside down". */ @@ -375,16 +378,15 @@ public class DisplayLayout { insetsState.getDisplayFrame(), WindowInsets.Type.navigationBars(), false /* ignoreVisibility */); - outInsets.set(insets.left, insets.top, insets.right, insets.bottom); int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); int navBarSize = getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); if (position == NAV_BAR_BOTTOM) { - outInsets.bottom = Math.max(outInsets.bottom , navBarSize); + outInsets.bottom = Math.max(insets.bottom , navBarSize); } else if (position == NAV_BAR_RIGHT) { - outInsets.right = Math.max(outInsets.right , navBarSize); + outInsets.right = Math.max(insets.right , navBarSize); } else if (position == NAV_BAR_LEFT) { - outInsets.left = Math.max(outInsets.left , navBarSize); + outInsets.left = Math.max(insets.left , navBarSize); } } 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 ab8e7e63ef7f..f70d3aec9ec8 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 @@ -63,8 +63,10 @@ import com.android.internal.policy.DockedDividerUtils; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.InteractionJankMonitorUtils; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; @@ -104,6 +106,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private final Rect mWinBounds2 = new Rect(); private final SplitLayoutHandler mSplitLayoutHandler; private final SplitWindowManager mSplitWindowManager; + private final DisplayController mDisplayController; private final DisplayImeController mDisplayImeController; private final ImePositionProcessor mImePositionProcessor; private final ResizingEffectPolicy mSurfaceEffectPolicy; @@ -128,13 +131,14 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public SplitLayout(String windowName, Context context, Configuration configuration, SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, - DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer, - int parallaxType) { + DisplayController displayController, DisplayImeController displayImeController, + ShellTaskOrganizer taskOrganizer, int parallaxType) { mContext = context.createConfigurationContext(configuration); mOrientation = configuration.orientation; mRotation = configuration.windowConfiguration.getRotation(); mDensity = configuration.densityDpi; mSplitLayoutHandler = splitLayoutHandler; + mDisplayController = displayController; mDisplayImeController = displayImeController; mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration, parentContainerCallbacks); @@ -145,7 +149,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange updateDividerConfig(mContext); mRootBounds.set(configuration.windowConfiguration.getBounds()); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); resetDividerPosition(); mDimNonImeSide = mContext.getResources().getBoolean(R.bool.config_dimNonImeAttachedSide); @@ -314,7 +318,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mRotation = rotation; mDensity = density; mUiMode = uiMode; - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); updateDividerConfig(mContext); initDividerPosition(mTempRect); updateInvisibleRect(); @@ -324,7 +328,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Rotate the layout to specific rotation and calculate new bounds. The stable insets value * should be calculated by display layout. */ - public void rotateTo(int newRotation, Rect stableInsets) { + public void rotateTo(int newRotation) { final int rotationDelta = (newRotation - mRotation + 4) % 4; final boolean changeOrient = (rotationDelta % 2) != 0; @@ -337,7 +341,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // We only need new bounds here, other configuration should be update later. mTempRect.set(mRootBounds); mRootBounds.set(tmpRect); - mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, stableInsets); + mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds); initDividerPosition(mTempRect); } @@ -548,10 +552,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return mDividerSnapAlgorithm.calculateSnapTarget(position, velocity, hardDismiss); } - private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds, - @Nullable Rect stableInsets) { + private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds) { final boolean isLandscape = isLandscape(rootBounds); - final Rect insets = stableInsets != null ? stableInsets : getDisplayInsets(context); + final Rect insets = getDisplayStableInsets(context); // Make split axis insets value same as the larger one to avoid bounds1 and bounds2 // have difference for avoiding size-compat mode when switching unresizable apps in @@ -634,7 +637,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, Consumer<Rect> finishCallback) { final boolean isLandscape = isLandscape(); - final Rect insets = getDisplayInsets(mContext); + final Rect insets = getDisplayStableInsets(mContext); insets.set(isLandscape ? insets.left : 0, isLandscape ? 0 : insets.top, isLandscape ? insets.right : 0, isLandscape ? 0 : insets.bottom); @@ -705,13 +708,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return animator; } - private static Rect getDisplayInsets(Context context) { - return context.getSystemService(WindowManager.class) - .getMaximumWindowMetrics() - .getWindowInsets() - .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() - | WindowInsets.Type.displayCutout()) - .toRect(); + private Rect getDisplayStableInsets(Context context) { + final DisplayLayout displayLayout = + mDisplayController.getDisplayLayout(context.getDisplayId()); + return displayLayout != null + ? displayLayout.stableInsets() + : context.getSystemService(WindowManager.class) + .getMaximumWindowMetrics() + .getWindowInsets() + .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() + | WindowInsets.Type.displayCutout()) + .toRect(); } private static boolean isLandscape(Rect bounds) { @@ -784,7 +791,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private int getSmallestWidthDp(Rect bounds) { mTempRect.set(bounds); - mTempRect.inset(getDisplayInsets(mContext)); + mTempRect.inset(getDisplayStableInsets(mContext)); final int minWidth = Math.min(mTempRect.width(), mTempRect.height()); final float density = mContext.getResources().getDisplayMetrics().density; return (int) (minWidth / density); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 5a9c25f7a710..c491fed5d896 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -70,6 +70,8 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.fullscreen.FullscreenTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; +import com.android.wm.shell.keyguard.KeyguardTransitionHandler; +import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -77,8 +79,6 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipTouchHandler; -import com.android.wm.shell.keyguard.KeyguardTransitionHandler; -import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; @@ -102,13 +102,13 @@ import com.android.wm.shell.unfold.UnfoldAnimationController; import com.android.wm.shell.unfold.UnfoldTransitionHandler; import com.android.wm.shell.windowdecor.WindowDecorViewModel; +import java.util.Optional; + import dagger.BindsOptionalOf; import dagger.Lazy; import dagger.Module; import dagger.Provides; -import java.util.Optional; - /** * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only * accessible from components within the WM subcomponent (can be explicitly exposed to the @@ -548,13 +548,15 @@ public abstract class WMShellBaseModule { @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler, @ShellAnimationThread ShellExecutor animExecutor, - ShellCommandHandler shellCommandHandler) { + ShellCommandHandler shellCommandHandler, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) { // TODO(b/238217847): Force override shell init if registration is disabled shellInit = new ShellInit(mainExecutor); } return new Transitions(context, shellInit, shellController, organizer, pool, - displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler); + displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler, + rootTaskDisplayAreaOrganizer); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java index c25352b6e57d..cef7e1666331 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java @@ -17,31 +17,25 @@ package com.android.wm.shell.keyguard; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; -import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; -import static android.view.WindowManager.TRANSIT_NONE; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_SLEEP; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; +import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; -import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; +import static android.view.WindowManager.TRANSIT_SLEEP; import static com.android.wm.shell.util.TransitionUtil.isOpeningType; -import static com.android.wm.shell.util.TransitionUtil.isClosingType; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.RemoteException; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; @@ -56,8 +50,6 @@ import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; -import java.util.Map; - /** * The handler for Keyguard enter/exit and occlude/unocclude animations. * @@ -70,7 +62,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler private final Handler mMainHandler; private final ShellExecutor mMainExecutor; - private final Map<IBinder, IRemoteTransition> mStartedTransitions = new ArrayMap<>(); + private final ArrayMap<IBinder, StartedTransition> mStartedTransitions = new ArrayMap<>(); /** * Local IRemoteTransition implementations registered by the keyguard service. @@ -81,6 +73,18 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler private IRemoteTransition mOccludeByDreamTransition = null; private IRemoteTransition mUnoccludeTransition = null; + private final class StartedTransition { + final TransitionInfo mInfo; + final SurfaceControl.Transaction mFinishT; + final IRemoteTransition mPlayer; + + public StartedTransition(TransitionInfo info, + SurfaceControl.Transaction finishT, IRemoteTransition player) { + mInfo = info; + mFinishT = finishT; + mPlayer = player; + } + } public KeyguardTransitionHandler( @NonNull ShellInit shellInit, @NonNull Transitions transitions, @@ -105,10 +109,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler } public static boolean handles(TransitionInfo info) { - return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0 - || (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0 - || info.getType() == TRANSIT_KEYGUARD_OCCLUDE - || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE; + return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0; } @Override @@ -120,39 +121,14 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler return false; } - boolean hasOpeningOcclude = false; - boolean hasClosingOcclude = false; - boolean hasOpeningDream = false; - boolean hasClosingApp = false; - - // Check for occluding/dream/closing apps - for (int i = info.getChanges().size() - 1; i >= 0; i--) { - final TransitionInfo.Change change = info.getChanges().get(i); - if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) { - continue; - } else if (isOpeningType(change.getMode())) { - hasOpeningOcclude |= change.hasFlags(FLAG_OCCLUDES_KEYGUARD); - hasOpeningDream |= (change.getTaskInfo() != null - && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM); - } else if (isClosingType(change.getMode())) { - hasClosingOcclude |= change.hasFlags(FLAG_OCCLUDES_KEYGUARD); - hasClosingApp = true; - } - } - // Choose a transition applicable for the changes and keyguard state. if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { return startAnimation(mExitTransition, "going-away", transition, info, startTransaction, finishTransaction, finishCallback); } - if (hasOpeningOcclude || info.getType() == TRANSIT_KEYGUARD_OCCLUDE) { - if (hasClosingOcclude) { - // Transitions between apps on top of the keyguard can use the default handler. - // WM sends a final occlude status update after the transition is finished. - return false; - } - if (hasOpeningDream) { + if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) { + if (hasOpeningDream(info)) { return startAnimation(mOccludeByDreamTransition, "occlude-by-dream", transition, info, startTransaction, finishTransaction, finishCallback); @@ -161,12 +137,12 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler "occlude", transition, info, startTransaction, finishTransaction, finishCallback); } - } else if (hasClosingApp || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) { + } else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { return startAnimation(mUnoccludeTransition, "unocclude", transition, info, startTransaction, finishTransaction, finishCallback); } else { - Log.w(TAG, "Failed to play: " + info); + Log.i(TAG, "Refused to play keyguard transition: " + info); return false; } } @@ -185,13 +161,17 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler @Override public void onTransitionFinished( WindowContainerTransaction wct, SurfaceControl.Transaction sct) { + if (sct != null) { + finishTransaction.merge(sct); + } mMainExecutor.execute(() -> { mStartedTransitions.remove(transition); finishCallback.onTransitionFinished(wct, null); }); } }); - mStartedTransitions.put(transition, remoteHandler); + mStartedTransitions.put(transition, + new StartedTransition(info, finishTransaction, remoteHandler)); } catch (RemoteException e) { Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e); return false; @@ -204,20 +184,35 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler public void mergeAnimation(@NonNull IBinder nextTransition, @NonNull TransitionInfo nextInfo, @NonNull SurfaceControl.Transaction nextT, @NonNull IBinder currentTransition, @NonNull TransitionFinishCallback nextFinishCallback) { - final IRemoteTransition playing = mStartedTransitions.get(currentTransition); - + final StartedTransition playing = mStartedTransitions.get(currentTransition); if (playing == null) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "unknown keyguard transition %s", currentTransition); return; } - - if (nextInfo.getType() == TRANSIT_SLEEP) { + if ((nextInfo.getFlags() & WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING) != 0 + && (playing.mInfo.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { + // Keyguard unlocking has been canceled. Merge the unlock and re-lock transitions to + // avoid a flicker where we flash one frame with the screen fully unlocked. + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "canceling keyguard exit transition %s", currentTransition); + playing.mFinishT.merge(nextT); + try { + playing.mPlayer.mergeAnimation(nextTransition, nextInfo, nextT, currentTransition, + new FakeFinishCallback()); + } catch (RemoteException e) { + // There is no good reason for this to happen because the player is a local object + // implementing an AIDL interface. + Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e); + } + nextFinishCallback.onTransitionFinished(null, null); + } else if (nextInfo.getType() == TRANSIT_SLEEP) { // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep // token is held. In cases where keyguard is showing, we are running the animation for // the device sleeping/waking, so it's best to ignore this and keep playing anyway. return; - } else { + } else if (handles(nextInfo)) { + // In all other cases, fast-forward to let the next queued transition start playing. finishAnimationImmediately(currentTransition, playing); } } @@ -225,7 +220,7 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler @Override public void onTransitionConsumed(IBinder transition, boolean aborted, SurfaceControl.Transaction finishTransaction) { - final IRemoteTransition playing = mStartedTransitions.remove(transition); + final StartedTransition playing = mStartedTransitions.remove(transition); if (playing != null) { finishAnimationImmediately(transition, playing); } @@ -238,13 +233,26 @@ public class KeyguardTransitionHandler implements Transitions.TransitionHandler return null; } - private void finishAnimationImmediately(IBinder transition, IRemoteTransition playing) { + private static boolean hasOpeningDream(@NonNull TransitionInfo info) { + for (int i = info.getChanges().size() - 1; i >= 0; i--) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (isOpeningType(change.getMode()) + && change.getTaskInfo() != null + && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) { + return true; + } + } + return false; + } + + private void finishAnimationImmediately(IBinder transition, StartedTransition playing) { final IBinder fakeTransition = new Binder(); final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0); final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction(); final FakeFinishCallback fakeFinishCb = new FakeFinishCallback(); try { - playing.mergeAnimation(fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb); + playing.mPlayer.mergeAnimation( + fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb); } catch (RemoteException e) { // There is no good reason for this to happen because the player is a local object // implementing an AIDL interface. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index db7c3fc9c9ec..07d11cf9f63c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -438,6 +438,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "startSwipePipToHome: %s, state=%s", componentName, mPipTransitionState); mPipTransitionState.setInSwipePipToHomeTransition(true); sendOnPipTransitionStarted(TRANSITION_DIRECTION_TO_PIP); setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo); @@ -450,6 +452,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds, SurfaceControl overlay) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "stopSwipePipToHome: %s, state=%s", componentName, mPipTransitionState); // do nothing if there is no startSwipePipToHome being called before if (!mPipTransitionState.getInSwipePipToHomeTransition()) { return; @@ -522,23 +526,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "exitPip: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (isLaunchIntoPipTask()) { exitLaunchIntoPipTask(wct); return; } - if (ENABLE_SHELL_TRANSITIONS) { - if (requestEnterSplit && mSplitScreenOptional.isPresent()) { - mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo, - isPipTopLeft() - ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); - mPipTransitionController.startExitTransition( - TRANSIT_EXIT_PIP_TO_SPLIT, wct, null /* destinationBounds */); - return; - } - } - final Rect displayBounds = mPipBoundsState.getDisplayBounds(); final Rect destinationBounds = new Rect(displayBounds); final int direction = syncWithSplitScreenBounds(destinationBounds, requestEnterSplit) @@ -547,10 +542,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // For exiting to fullscreen, the windowing mode of task will be changed to fullscreen // until the animation is finished. Otherwise if the activity is resumed and focused at the // begin of aniamtion, the app may do something too early to distub the animation. - final boolean toFullscreen = destinationBounds.equals(displayBounds); - if (Transitions.SHELL_TRANSITIONS_ROTATION || (Transitions.ENABLE_SHELL_TRANSITIONS - && !toFullscreen)) { + if (Transitions.SHELL_TRANSITIONS_ROTATION) { // When exit to fullscreen with Shell transition enabled, we update the Task windowing // mode directly so that it can also trigger display rotation and visibility update in // the same transition if there will be any. @@ -559,6 +552,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // destinationBounds calculated above will be incorrect if this is with rotation. wct.setBounds(mToken, null); } else { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "exitPip: %s, dest=%s", mTaskInfo.topActivity, destinationBounds); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, @@ -580,9 +575,29 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); if (Transitions.ENABLE_SHELL_TRANSITIONS) { + if (requestEnterSplit && mSplitScreenOptional.isPresent()) { + wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); + mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo, + isPipToTopLeft() + ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); + mPipTransitionController.startExitTransition( + TRANSIT_EXIT_PIP_TO_SPLIT, wct, destinationBounds); + return; + } + + if (mSplitScreenOptional.isPresent()) { + // If pip activity will reparent to origin task case and if the origin task still + // under split root, apply exit split transaction to make it expand to fullscreen. + SplitScreenController split = mSplitScreenOptional.get(); + if (split.isTaskInSplitScreen(mTaskInfo.lastParentTaskIdBeforePip)) { + split.prepareExitSplitScreen(wct, split.getStageOfTask( + mTaskInfo.lastParentTaskIdBeforePip)); + } + } mPipTransitionController.startExitTransition(TRANSIT_EXIT_PIP, wct, destinationBounds); return; } + if (mSplitScreenOptional.isPresent()) { // If pip activity will reparent to origin task case and if the origin task still under // split root, just exit split screen here to ensure it could expand to fullscreen. @@ -656,9 +671,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, animator.setInterpolator(Interpolators.ALPHA_OUT); animator.start(); mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "removePip: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); } private void removePipImmediately() { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "removePipImmediately: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); if (Transitions.ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setBounds(mToken, null); @@ -723,6 +742,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } mPipUiEventLoggerLogger.log(uiEventEnum); + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskAppeared: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); if (mPipTransitionState.getInSwipePipToHomeTransition()) { if (!mWaitForFixedRotation) { onEndOfSwipePipToHomeTransition(); @@ -774,6 +795,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void onTaskAppearedWithFixedRotation(int animationType) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskAppearedWithFixedRotation: %s, state=%s animationType=%d", + mTaskInfo.topActivity, mPipTransitionState, animationType); if (animationType == ANIM_TYPE_ALPHA) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Defer entering PiP alpha animation, fixed rotation is ongoing", TAG); @@ -920,6 +944,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskVanished: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState); if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) { return; } @@ -961,6 +987,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipBoundsState.setOverrideMinSize( mPipBoundsAlgorithm.getMinimalSize(info.topActivityInfo)); final PictureInPictureParams newParams = info.pictureInPictureParams; + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onTaskInfoChanged: %s, state=%s oldParams=%s newParams=%s", + mTaskInfo.topActivity, mPipTransitionState, mPictureInPictureParams, newParams); // mPictureInPictureParams is only null if there is no PiP if (newParams == null || mPictureInPictureParams == null) { @@ -1001,6 +1030,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onFixedRotationStarted(int displayId, int newRotation) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onFixedRotationStarted: %s, state=%s", mTaskInfo, mPipTransitionState); mNextRotation = newRotation; mWaitForFixedRotation = true; @@ -1022,6 +1053,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onFixedRotationFinished(int displayId) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onFixedRotationFinished: %s, state=%s", mTaskInfo, mPipTransitionState); if (!mWaitForFixedRotation) { return; } @@ -1057,6 +1090,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** Called when exiting PIP transition is finished to do the state cleanup. */ void onExitPipFinished(TaskInfo info) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onExitPipFinished: %s, state=%s leash=%s", + info.topActivity, mPipTransitionState, mLeash); if (mLeash == null) { // TODO(239461594): Remove once the double call to onExitPipFinished() is fixed ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, @@ -1108,6 +1144,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** Explicitly set the visibility of PiP window. */ public void setPipVisibility(boolean visible) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "setPipVisibility: %s, state=%s visible=%s", + mTaskInfo.topActivity, mPipTransitionState, visible); if (!isInPip()) { return; } @@ -1634,17 +1673,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } - private boolean isPipTopLeft() { - if (!mSplitScreenOptional.isPresent()) { - return false; - } - final Rect topLeft = new Rect(); - final Rect bottomRight = new Rect(); - mSplitScreenOptional.get().getStageBounds(topLeft, bottomRight); - - return topLeft.contains(mPipBoundsState.getBounds()); - } - private boolean isPipToTopLeft() { if (!mSplitScreenOptional.isPresent()) { return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 7b8e4778ec98..86b0f33ad54c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -308,8 +308,9 @@ public class PipTransition extends PipTransitionController { @Override public void end() { Animator animator = mPipAnimationController.getCurrentAnimator(); - if (animator == null) return; - animator.end(); + if (animator != null && animator.isRunning()) { + animator.end(); + } } @Override @@ -815,6 +816,12 @@ public class PipTransition extends PipTransitionController { final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo(); final SurfaceControl leash = pipChange.getLeash(); final int startRotation = pipChange.getStartRotation(); + // Check again in case some callers use startEnterAnimation directly so the flag was not + // set in startAnimation, e.g. from DefaultMixedHandler. + if (!mInFixedRotation) { + mEndFixedRotation = pipChange.getEndFixedRotation(); + mInFixedRotation = mEndFixedRotation != ROTATION_UNDEFINED; + } final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation(); setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams, @@ -843,7 +850,7 @@ public class PipTransition extends PipTransitionController { && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash, - sourceHintRect, destinationBounds, rotationDelta, taskInfo); + sourceHintRect, destinationBounds, taskInfo); return; } @@ -934,8 +941,15 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull SurfaceControl leash, @Nullable Rect sourceHintRect, - @NonNull Rect destinationBounds, int rotationDelta, + @NonNull Rect destinationBounds, @NonNull ActivityManager.RunningTaskInfo pipTaskInfo) { + if (mInFixedRotation) { + // If rotation changes when returning to home, the transition should contain both the + // entering PiP and the display change (PipController#startSwipePipToHome has updated + // the display layout to new rotation). So it is not expected to see fixed rotation. + ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "%s: SwipePipToHome should not use fixed rotation %d", TAG, mEndFixedRotation); + } final SurfaceControl swipePipToHomeOverlay = mPipOrganizer.mSwipePipToHomeOverlay; if (swipePipToHomeOverlay != null) { // Launcher fade in the overlay on top of the fullscreen Task. It is possible we @@ -946,12 +960,7 @@ public class PipTransition extends PipTransitionController { mPipOrganizer.mSwipePipToHomeOverlay = null; } - Rect sourceBounds = pipTaskInfo.configuration.windowConfiguration.getBounds(); - if (!Transitions.SHELL_TRANSITIONS_ROTATION && rotationDelta % 2 == 1) { - // PipController#startSwipePipToHome has updated the display layout to new rotation, - // so flip the source bounds to match the same orientation. - sourceBounds = new Rect(0, 0, sourceBounds.height(), sourceBounds.width()); - } + final Rect sourceBounds = pipTaskInfo.configuration.windowConfiguration.getBounds(); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, @@ -980,12 +989,7 @@ public class PipTransition extends PipTransitionController { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, @NonNull TaskInfo taskInfo) { - final int changeSize = info.getChanges().size(); - if (changeSize < 4) { - throw new RuntimeException( - "Got an exit-pip-to-split transition with unexpected change-list"); - } - for (int i = changeSize - 1; i >= 0; i--) { + for (int i = info.getChanges().size() - 1; i >= 0; i--) { final TransitionInfo.Change change = info.getChanges().get(i); final int mode = change.getMode(); @@ -1072,10 +1076,28 @@ public class PipTransition extends PipTransitionController { } final float alphaStart = show ? 0 : 1; final float alphaEnd = show ? 1 : 0; + final PipAnimationController.PipTransactionHandler transactionHandler = + new PipAnimationController.PipTransactionHandler() { + @Override + public boolean handlePipTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, Rect destinationBounds, float alpha) { + if (alpha == 0) { + if (show) { + tx.setPosition(leash, destinationBounds.left, destinationBounds.top); + } else { + // Put PiP out of the display so it won't block touch when it is hidden. + final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds(); + final int max = Math.max(displayBounds.width(), displayBounds.height()); + tx.setPosition(leash, max, max); + } + } + return false; + } + }; mPipAnimationController .getAnimator(taskInfo, leash, mPipBoundsState.getBounds(), alphaStart, alphaEnd) .setTransitionDirection(TRANSITION_DIRECTION_SAME) - .setPipAnimationCallback(mPipAnimationCallback) + .setPipTransactionHandler(transactionHandler) .setDuration(mEnterExitAnimationDuration) .start(); mHasFadeOut = !show; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 2fff0e469f3d..e1bcd70c256b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -38,6 +38,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -223,6 +224,13 @@ public abstract class PipTransitionController implements Transitions.TransitionH return false; } + /** Whether a particular package is same as current pip package. */ + public boolean isInPipPackage(String packageName) { + final TaskInfo inPipTask = mPipOrganizer.getTaskInfo(); + return packageName != null && inPipTask != null + && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); + } + /** Add PiP-related changes to `outWCT` for the given request. */ public void augmentRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java index db6138a0891f..b5f44835802f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionState.java @@ -140,6 +140,24 @@ public class PipTransitionState { return state == ENTERING_PIP; } + private String stateToString() { + switch (mState) { + case UNDEFINED: return "undefined"; + case TASK_APPEARED: return "task-appeared"; + case ENTRY_SCHEDULED: return "entry-scheduled"; + case ENTERING_PIP: return "entering-pip"; + case ENTERED_PIP: return "entered-pip"; + case EXITING_PIP: return "exiting-pip"; + } + throw new IllegalStateException("Unknown state: " + mState); + } + + @Override + public String toString() { + return String.format("PipTransitionState(mState=%s, mInSwipePipToHomeTransition=%b)", + stateToString(), mInSwipePipToHomeTransition); + } + public interface OnPipTransitionStateChangedListener { void onPipTransitionStateChanged(@TransitionState int oldState, @TransitionState int newState); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index e7a1395f541c..5e1b6becfa45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -31,6 +31,8 @@ import android.os.RemoteException; import android.util.Size; import android.view.MotionEvent; import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import com.android.internal.protolog.common.ProtoLog; @@ -131,6 +133,8 @@ public class PhonePipMenuController implements PipMenuController { private PipMenuView mPipMenuView; + private SurfaceControl mLeash; + private ActionListener mMediaActionListener = new ActionListener() { @Override public void onMediaActionsChanged(List<RemoteAction> mediaActions) { @@ -166,6 +170,7 @@ public class PhonePipMenuController implements PipMenuController { */ @Override public void attach(SurfaceControl leash) { + mLeash = leash; attachPipMenuView(); } @@ -176,6 +181,7 @@ public class PhonePipMenuController implements PipMenuController { public void detach() { hideMenu(); detachPipMenuView(); + mLeash = null; } void attachPipMenuView() { @@ -185,6 +191,36 @@ public class PhonePipMenuController implements PipMenuController { } mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler, mSplitScreenController, mPipUiEventLogger); + mPipMenuView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + v.getViewRootImpl().addSurfaceChangedCallback( + new ViewRootImpl.SurfaceChangedCallback() { + @Override + public void surfaceCreated(SurfaceControl.Transaction t) { + final SurfaceControl sc = getSurfaceControl(); + if (sc != null) { + t.reparent(sc, mLeash); + // make menu on top of the surface + t.setLayer(sc, Integer.MAX_VALUE); + } + } + + @Override + public void surfaceReplaced(SurfaceControl.Transaction t) { + } + + @Override + public void surfaceDestroyed() { + } + }); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(mContext, MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), 0, SHELL_ROOT_LAYER_PIP); @@ -321,30 +357,10 @@ public class PhonePipMenuController implements PipMenuController { return; } - // If there is no pip leash supplied, that means the PiP leash is already finalized - // resizing and the PiP menu is also resized. We then want to do a scale from the current - // new menu bounds. + // TODO(b/286307861) transaction should be applied outside of PiP menu controller if (pipLeash != null && t != null) { - mPipMenuView.getBoundsOnScreen(mTmpSourceBounds); - } else { - mTmpSourceBounds.set(0, 0, destinationBounds.width(), destinationBounds.height()); + t.apply(); } - - mTmpSourceRectF.set(mTmpSourceBounds); - mTmpDestinationRectF.set(destinationBounds); - mMoveTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); - final SurfaceControl surfaceControl = getSurfaceControl(); - if (surfaceControl == null) { - return; - } - final SurfaceControl.Transaction menuTx = - mSurfaceControlTransactionFactory.getTransaction(); - menuTx.setMatrix(surfaceControl, mMoveTransform, mTmpTransform); - if (pipLeash != null && t != null) { - // Merge the two transactions, vsyncId has been set on menuTx. - menuTx.merge(t); - } - menuTx.apply(); } /** @@ -362,18 +378,10 @@ public class PhonePipMenuController implements PipMenuController { return; } - final SurfaceControl surfaceControl = getSurfaceControl(); - if (surfaceControl == null) { - return; - } - final SurfaceControl.Transaction menuTx = - mSurfaceControlTransactionFactory.getTransaction(); - menuTx.setCrop(surfaceControl, destinationBounds); + // TODO(b/286307861) transaction should be applied outside of PiP menu controller if (pipLeash != null && t != null) { - // Merge the two transactions, vsyncId has been set on menuTx. - menuTx.merge(t); + t.apply(); } - menuTx.apply(); } private boolean checkPipMenuState() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 6a861ce97431..65727b6145e4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -574,6 +574,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onActivityPinned: %s", packageName); mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mAppOpsListener.onActivityPinned(packageName); @@ -585,6 +587,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(mContext); final ComponentName topActivity = topPipActivityInfo.first; + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onActivityUnpinned: %s", topActivity); mTouchHandler.onActivityUnpinned(topActivity); mAppOpsListener.onActivityUnpinned(); mPipInputConsumer.unregisterInputConsumer(); @@ -593,6 +597,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "onActivityRestartAttempt: %s", task.topActivity); if (task.getWindowingMode() != WINDOWING_MODE_PINNED) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 167c0321d3ad..779c539a2097 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -45,6 +45,7 @@ import android.content.Intent; import android.graphics.Color; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -513,13 +514,19 @@ public class PipMenuView extends FrameLayout { final boolean isCloseAction = mCloseAction != null && Objects.equals( mCloseAction.getActionIntent(), action.getActionIntent()); - // TODO: Check if the action drawable has changed before we reload it - action.getIcon().loadDrawableAsync(mContext, d -> { - if (d != null) { - d.setTint(Color.WHITE); - actionView.setImageDrawable(d); - } - }, mMainHandler); + final int iconType = action.getIcon().getType(); + if (iconType == Icon.TYPE_URI || iconType == Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // Disallow loading icon from content URI + actionView.setImageDrawable(null); + } else { + // TODO: Check if the action drawable has changed before we reload it + action.getIcon().loadDrawableAsync(mContext, d -> { + if (d != null) { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + } + }, mMainHandler); + } actionView.setCustomCloseBackgroundVisibility( isCloseAction ? View.VISIBLE : View.GONE); actionView.setContentDescription(action.getContentDescription()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 281cae5e4ffa..abe2db094a5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -70,6 +70,7 @@ public class PipResizeGestureHandler { private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final PipMotionHelper mMotionHelper; private final PipBoundsState mPipBoundsState; + private final PipTouchState mPipTouchState; private final PipTaskOrganizer mPipTaskOrganizer; private final PhonePipMenuController mPhonePipMenuController; private final PipDismissTargetHandler mPipDismissTargetHandler; @@ -104,7 +105,6 @@ public class PipResizeGestureHandler { private boolean mAllowGesture; private boolean mIsAttached; private boolean mIsEnabled; - private boolean mEnableTouch; private boolean mEnablePinchResize; private boolean mEnableDragCornerResize; private boolean mIsSysUiStateValid; @@ -122,7 +122,8 @@ public class PipResizeGestureHandler { public PipResizeGestureHandler(Context context, PipBoundsAlgorithm pipBoundsAlgorithm, PipBoundsState pipBoundsState, PipMotionHelper motionHelper, - PipTaskOrganizer pipTaskOrganizer, PipDismissTargetHandler pipDismissTargetHandler, + PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer, + PipDismissTargetHandler pipDismissTargetHandler, Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController, ShellExecutor mainExecutor) { @@ -132,6 +133,7 @@ public class PipResizeGestureHandler { mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mMotionHelper = motionHelper; + mPipTouchState = pipTouchState; mPipTaskOrganizer = pipTaskOrganizer; mPipDismissTargetHandler = pipDismissTargetHandler; mMovementBoundsSupplier = movementBoundsSupplier; @@ -139,7 +141,6 @@ public class PipResizeGestureHandler { mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; mPinchResizingAlgorithm = new PipPinchResizingAlgorithm(); - mEnableTouch = true; mUpdateResizeBoundsCallback = (rect) -> { mUserResizeBounds.set(rect); @@ -250,8 +251,8 @@ public class PipResizeGestureHandler { return; } - if (!mEnableTouch) { - // No need to handle anything if touches are not enabled for resizing. + if (!mPipTouchState.getAllowInputEvents()) { + // No need to handle anything if touches are not enabled return; } @@ -588,13 +589,13 @@ public class PipResizeGestureHandler { mLastResizeBounds, movementBounds); mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction); - // disable the resizing until the final bounds are updated - mEnableTouch = false; + // disable any touch events beyond resizing too + mPipTouchState.setAllowInputEvents(false); mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> { - // reset the pinch resizing to its default state - mEnableTouch = true; + // enable touch events + mPipTouchState.setAllowInputEvents(true); }); } else { mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java index 82fe38ccc7b3..7971c049ff3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java @@ -135,14 +135,14 @@ public class PipSizeSpecHandler { maxWidth = (int) (mOptimizedAspectRatio * shorterLength + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1 + aspectRatio)); - maxHeight = (int) (maxWidth / aspectRatio); + maxHeight = Math.round(maxWidth / aspectRatio); } else { if (aspectRatio > 1f) { maxWidth = shorterLength; - maxHeight = (int) (maxWidth / aspectRatio); + maxHeight = Math.round(maxWidth / aspectRatio); } else { maxHeight = shorterLength; - maxWidth = (int) (maxHeight * aspectRatio); + maxWidth = Math.round(maxHeight * aspectRatio); } } @@ -165,10 +165,9 @@ public class PipSizeSpecHandler { Size maxSize = this.getMaxSize(aspectRatio); - int defaultWidth = Math.max((int) (maxSize.getWidth() * mDefaultSizePercent), + int defaultWidth = Math.max(Math.round(maxSize.getWidth() * mDefaultSizePercent), minSize.getWidth()); - int defaultHeight = Math.max((int) (maxSize.getHeight() * mDefaultSizePercent), - minSize.getHeight()); + int defaultHeight = Math.round(defaultWidth / aspectRatio); return new Size(defaultWidth, defaultHeight); } @@ -188,16 +187,16 @@ public class PipSizeSpecHandler { Size maxSize = this.getMaxSize(aspectRatio); - int minWidth = (int) (maxSize.getWidth() * mMinimumSizePercent); - int minHeight = (int) (maxSize.getHeight() * mMinimumSizePercent); + int minWidth = Math.round(maxSize.getWidth() * mMinimumSizePercent); + int minHeight = Math.round(maxSize.getHeight() * mMinimumSizePercent); // make sure the calculated min size is not smaller than the allowed default min size if (aspectRatio > 1f) { - minHeight = (int) Math.max(minHeight, mDefaultMinSize); - minWidth = (int) (minHeight * aspectRatio); + minHeight = Math.max(minHeight, mDefaultMinSize); + minWidth = Math.round(minHeight * aspectRatio); } else { - minWidth = (int) Math.max(minWidth, mDefaultMinSize); - minHeight = (int) (minWidth / aspectRatio); + minWidth = Math.max(minWidth, mDefaultMinSize); + minHeight = Math.round(minWidth / aspectRatio); } return new Size(minWidth, minHeight); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 466da0e85358..2c4f76b1f34b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -199,11 +199,6 @@ public class PipTouchHandler { mMotionHelper = pipMotionHelper; mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger, mMotionHelper, mainExecutor); - mPipResizeGestureHandler = - new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, - mMotionHelper, pipTaskOrganizer, mPipDismissTargetHandler, - this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger, - menuController, mainExecutor); mTouchState = new PipTouchState(ViewConfiguration.get(context), () -> { if (mPipBoundsState.isStashed()) { @@ -220,6 +215,11 @@ public class PipTouchHandler { }, menuController::hideMenu, mainExecutor); + mPipResizeGestureHandler = + new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, + mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler, + this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger, + menuController, mainExecutor); mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState, mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(), this::onAccessibilityShowMenu, this::updateMovementBounds, @@ -556,6 +556,11 @@ public class PipTouchHandler { return true; } + // do not process input event if not allowed + if (!mTouchState.getAllowInputEvents()) { + return true; + } + MotionEvent ev = (MotionEvent) inputEvent; if (!mPipBoundsState.isStashed() && mPipResizeGestureHandler.willStartResizeGesture(ev)) { // Initialize the touch state for the gesture, but immediately reset to invalidate the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java index d7d69f27f9f8..7f62c629c7f2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java @@ -37,7 +37,7 @@ public class PipTouchState { private static final boolean DEBUG = false; @VisibleForTesting - public static final long DOUBLE_TAP_TIMEOUT = 200; + public static final long DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout(); static final long HOVER_EXIT_TIMEOUT = 50; private final ShellExecutor mMainExecutor; @@ -55,6 +55,9 @@ public class PipTouchState { private final PointF mLastDelta = new PointF(); private final PointF mVelocity = new PointF(); private boolean mAllowTouches = true; + + // Set to false to block both PipTouchHandler and PipResizeGestureHandler's input processing + private boolean mAllowInputEvents = true; private boolean mIsUserInteracting = false; // Set to true only if the multiple taps occur within the double tap timeout private boolean mIsDoubleTap = false; @@ -77,6 +80,20 @@ public class PipTouchState { } /** + * @return true if input processing is enabled for PiP in general. + */ + public boolean getAllowInputEvents() { + return mAllowInputEvents; + } + + /** + * @param allowInputEvents true to enable input processing for PiP in general. + */ + public void setAllowInputEvents(boolean allowInputEvents) { + mAllowInputEvents = allowInputEvents; + } + + /** * Resets this state. */ public void reset() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index c2869592dbb6..a612f5f09e88 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -35,6 +35,8 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; +import android.util.IntArray; +import android.util.Pair; import android.util.Slog; import android.view.Display; import android.view.IRecentsAnimationController; @@ -135,8 +137,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { @Override public WindowContainerTransaction handleRequest(IBinder transition, TransitionRequestInfo request) { - // do not directly handle requests. Only entry point should be via startRecentsTransition - // TODO: Only log an error if the transition is a recents transition + if (mControllers.isEmpty()) { + // Ignore if there is no running recents transition + return null; + } + final RecentsController controller = mControllers.get(mControllers.size() - 1); + controller.handleMidTransitionRequest(request); return null; } @@ -239,6 +245,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { /** The latest state that the recents animation is operating in. */ private int mState = STATE_NORMAL; + // Snapshots taken when a new display change transition is requested, prior to the display + // change being applied. This pending set of snapshots will only be applied when cancel is + // next called. + private Pair<int[], TaskSnapshot[]> mPendingPauseSnapshotsForCancel; + RecentsController(IRecentsAnimationRunner listener) { mInstanceId = System.identityHashCode(this); mListener = listener; @@ -290,6 +301,16 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { * "replace-with-screenshot" like behavior. */ private boolean sendCancelWithSnapshots() { + Pair<int[], TaskSnapshot[]> snapshots = mPendingPauseSnapshotsForCancel != null + ? mPendingPauseSnapshotsForCancel + : getSnapshotsForPausingTasks(); + return sendCancel(snapshots.first, snapshots.second); + } + + /** + * Snapshots the pausing tasks and returns the mapping of the taskId -> snapshot. + */ + private Pair<int[], TaskSnapshot[]> getSnapshotsForPausingTasks() { int[] taskIds = null; TaskSnapshot[] snapshots = null; if (mPausingTasks.size() > 0) { @@ -298,6 +319,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { try { for (int i = 0; i < mPausingTasks.size(); ++i) { TaskState state = mPausingTasks.get(0); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.sendCancel: Snapshotting task=%d", + mInstanceId, state.mTaskInfo.taskId); snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot( state.mTaskInfo.taskId, true /* updateCache */); } @@ -306,7 +330,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { snapshots = null; } } - return sendCancel(taskIds, snapshots); + return new Pair(taskIds, snapshots); } /** @@ -315,7 +339,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { private boolean sendCancel(@Nullable int[] taskIds, @Nullable TaskSnapshot[] taskSnapshots) { try { - final String cancelDetails = taskSnapshots != null ? " with snapshots" : ""; + final String cancelDetails = taskSnapshots != null ? "with snapshots" : ""; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.cancel: calling onAnimationCanceled %s", mInstanceId, cancelDetails); @@ -348,6 +372,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { mOpeningTasks = null; mInfo = null; mTransition = null; + mPendingPauseSnapshotsForCancel = null; mControllers.remove(this); } @@ -398,26 +423,28 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // About layering: we divide up the "layer space" into 3 regions (each the size of // the change count). This lets us categorize things into above/below/between // while maintaining their relative ordering. + final int belowLayers = info.getChanges().size(); + final int aboveLayers = info.getChanges().size() * 3; for (int i = 0; i < info.getChanges().size(); ++i) { final TransitionInfo.Change change = info.getChanges().get(i); final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (TransitionUtil.isWallpaper(change)) { final RemoteAnimationTarget target = TransitionUtil.newTarget(change, // wallpapers go into the "below" layer space - info.getChanges().size() - i, info, t, mLeashMap); + belowLayers - i, info, t, mLeashMap); wallpapers.add(target); // Make all the wallpapers opaque since we want them visible from the start t.setAlpha(target.leash, 1); } else if (leafTaskFilter.test(change)) { // start by putting everything into the "below" layer space. final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - info.getChanges().size() - i, info, t, mLeashMap); + belowLayers - i, info, t, mLeashMap); apps.add(target); if (TransitionUtil.isClosingType(change.getMode())) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " adding pausing taskId=%d", taskInfo.taskId); + " adding pausing leaf taskId=%d", taskInfo.taskId); // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(target.leash, info.getChanges().size() * 3 - i); + t.setLayer(target.leash, aboveLayers - i); mPausingTasks.add(new TaskState(change, target.leash)); if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { // This can only happen if we have a separate recents/home (3p launcher) @@ -430,17 +457,32 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { // There's a 3p launcher, so make sure recents goes above that. - t.setLayer(target.leash, info.getChanges().size() * 3 - i); + t.setLayer(target.leash, aboveLayers - i); } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { // do nothing } else if (TransitionUtil.isOpeningType(change.getMode())) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " adding opening taskId=%d", taskInfo.taskId); + " adding opening leaf taskId=%d", taskInfo.taskId); mOpeningTasks.add(new TaskState(change, target.leash)); } + } else if (taskInfo != null && TransitionInfo.isIndependent(change, info)) { + // Root tasks + if (TransitionUtil.isClosingType(change.getMode())) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " adding pausing taskId=%d", taskInfo.taskId); + // raise closing (pausing) task to "above" layer so it isn't covered + t.setLayer(change.getLeash(), aboveLayers - i); + mPausingTasks.add(new TaskState(change, null /* leash */)); + } else if (TransitionUtil.isOpeningType(change.getMode())) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " adding opening taskId=%d", taskInfo.taskId); + // Put into the "below" layer space. + t.setLayer(change.getLeash(), belowLayers - i); + mOpeningTasks.add(new TaskState(change, null /* leash */)); + } } else if (TransitionUtil.isDividerBar(change)) { final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - info.getChanges().size() - i, info, t, mLeashMap); + belowLayers - i, info, t, mLeashMap); // Add this as a app and we will separate them on launcher side by window type. apps.add(target); } @@ -460,6 +502,22 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { return true; } + /** + * Updates this controller when a new transition is requested mid-recents transition. + */ + void handleMidTransitionRequest(TransitionRequestInfo request) { + if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null) { + final TransitionRequestInfo.DisplayChange dispChange = request.getDisplayChange(); + if (dispChange.getStartRotation() != dispChange.getEndRotation()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.prepareForMerge: " + + "Snapshot due to requested display change", + mInstanceId); + mPendingPauseSnapshotsForCancel = getSnapshotsForPausingTasks(); + } + } + } + @SuppressLint("NewApi") void merge(TransitionInfo info, SurfaceControl.Transaction t, Transitions.TransitionFinishCallback finishCallback) { @@ -479,7 +537,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.merge", mInstanceId); + // Keep all tasks in one list because order matters. ArrayList<TransitionInfo.Change> openingTasks = null; + IntArray openingTaskIsLeafs = null; ArrayList<TransitionInfo.Change> closingTasks = null; mOpeningSeparateHome = false; TransitionInfo.Change recentsOpening = null; @@ -498,25 +558,29 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { cancel("task #" + taskInfo.taskId + " is always_on_top"); return; } - hasTaskChange = hasTaskChange || taskInfo != null; + final boolean isRootTask = taskInfo != null + && TransitionInfo.isIndependent(change, info); + hasTaskChange = hasTaskChange || isRootTask; final boolean isLeafTask = leafTaskFilter.test(change); if (TransitionUtil.isOpeningType(change.getMode())) { if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) { recentsOpening = change; - } else if (isLeafTask) { - if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + } else if (isRootTask || isLeafTask) { + if (isLeafTask && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { // This is usually a 3p launcher mOpeningSeparateHome = true; } if (openingTasks == null) { openingTasks = new ArrayList<>(); + openingTaskIsLeafs = new IntArray(); } openingTasks.add(change); + openingTaskIsLeafs.add(isLeafTask ? 1 : 0); } } else if (TransitionUtil.isClosingType(change.getMode())) { if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) { foundRecentsClosing = true; - } else if (isLeafTask) { + } else if (isRootTask || isLeafTask) { if (closingTasks == null) { closingTasks = new ArrayList<>(); } @@ -526,12 +590,25 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // Finish recents animation if the display is changed, so the default // transition handler can play the animation such as rotation effect. if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) { + // This call to cancel will use the screenshots taken preemptively in + // handleMidTransitionRequest() prior to the display changing cancel(mWillFinishToHome, true /* withScreenshots */, "display change"); return; } - // Don't consider order-only changes as changing apps. - if (!TransitionUtil.isOrderOnly(change)) { + // Don't consider order-only & non-leaf changes as changing apps. + if (!TransitionUtil.isOrderOnly(change) && isLeafTask) { hasChangingApp = true; + } else if (isLeafTask && taskInfo.topActivityType == ACTIVITY_TYPE_HOME + && !mRecentsTask.equals(change.getContainer())) { + // Unless it is a 3p launcher. This means that the 3p launcher was already + // visible (eg. the "pausing" task is translucent over the 3p launcher). + // Treat it as if we are "re-opening" the 3p launcher. + if (openingTasks == null) { + openingTasks = new ArrayList<>(); + openingTaskIsLeafs = new IntArray(); + } + openingTasks.add(change); + openingTaskIsLeafs.add(1); } } } @@ -580,7 +657,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { } final TaskState openingTask = mOpeningTasks.remove(openingIdx); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " pausing opening taskId=%d", openingTask.mTaskInfo.taskId); + " pausing opening %staskId=%d", openingTask.isLeaf() ? "leaf " : "", + openingTask.mTaskInfo.taskId); mPausingTasks.add(openingTask); didMergeThings = true; } @@ -590,35 +668,55 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { // Switching to some new tasks, add to mOpening and remove from mPausing. Also, // enter NEW_TASK state since this will start the switch-to animation. final int layer = mInfo.getChanges().size() * 3; - appearedTargets = new RemoteAnimationTarget[openingTasks.size()]; + int openingLeafCount = 0; + for (int i = 0; i < openingTaskIsLeafs.size(); ++i) { + openingLeafCount += openingTaskIsLeafs.get(i); + } + if (openingLeafCount > 0) { + appearedTargets = new RemoteAnimationTarget[openingLeafCount]; + } + int nextTargetIdx = 0; for (int i = 0; i < openingTasks.size(); ++i) { final TransitionInfo.Change change = openingTasks.get(i); + final boolean isLeaf = openingTaskIsLeafs.get(i) == 1; int pausingIdx = TaskState.indexOf(mPausingTasks, change); if (pausingIdx >= 0) { // Something is showing/opening a previously-pausing app. - appearedTargets[i] = TransitionUtil.newTarget( - change, layer, mPausingTasks.get(pausingIdx).mLeash); + if (isLeaf) { + appearedTargets[nextTargetIdx++] = TransitionUtil.newTarget( + change, layer, mPausingTasks.get(pausingIdx).mLeash); + } final TaskState pausingTask = mPausingTasks.remove(pausingIdx); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " opening pausing taskId=%d", pausingTask.mTaskInfo.taskId); + " opening pausing %staskId=%d", isLeaf ? "leaf " : "", + pausingTask.mTaskInfo.taskId); mOpeningTasks.add(pausingTask); // Setup hides opening tasks initially, so make it visible again (since we // are already showing it). t.show(change.getLeash()); t.setAlpha(change.getLeash(), 1.f); - } else { - // We are receiving new opening tasks, so convert to onTasksAppeared. - appearedTargets[i] = TransitionUtil.newTarget( + } else if (isLeaf) { + // We are receiving new opening leaf tasks, so convert to onTasksAppeared. + final RemoteAnimationTarget target = TransitionUtil.newTarget( change, layer, info, t, mLeashMap); + appearedTargets[nextTargetIdx++] = target; // reparent into the original `mInfo` since that's where we are animating. final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo); - t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash()); - t.setLayer(appearedTargets[i].leash, layer); + t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash()); + t.setLayer(target.leash, layer); // Hide the animation leash, let listener show it. - t.hide(appearedTargets[i].leash); + t.hide(target.leash); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " opening new leaf taskId=%d", target.taskId); + mOpeningTasks.add(new TaskState(change, target.leash)); + } else { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - " opening new taskId=%d", appearedTargets[i].taskId); - mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash)); + " opening new taskId=%d", change.getTaskInfo().taskId); + t.setLayer(change.getLeash(), layer); + // Setup hides opening tasks initially, so make it visible since recents + // is only animating the leafs. + t.show(change.getLeash()); + mOpeningTasks.add(new TaskState(change, null)); } } didMergeThings = true; @@ -803,7 +901,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { t.show(mOpeningTasks.get(i).mTaskSurface); } for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint) { + if (!sendUserLeaveHint && mPausingTasks.get(i).isLeaf()) { // This means recents is not *actually* finishing, so of course we gotta // do special stuff in WMCore to accommodate. wct.setDoNotPip(mPausingTasks.get(i).mToken); @@ -882,7 +980,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { /** The surface/leash of the task provided by Core. */ SurfaceControl mTaskSurface; - /** The (local) animation-leash created for this task. */ + /** The (local) animation-leash created for this task. Only non-null for leafs. */ + @Nullable SurfaceControl mLeash; TaskState(TransitionInfo.Change change, SurfaceControl leash) { @@ -901,6 +1000,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { return -1; } + boolean isLeaf() { + return mLeash != null; + } + public String toString() { return "" + mToken + " : " + mLeash; } 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 ea33a1f1b56d..e7a367f1992d 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 @@ -88,6 +88,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.draganddrop.DragAndDropPolicy; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.sysui.KeyguardChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; @@ -332,6 +333,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.getStageOfTask(taskId) != STAGE_TYPE_UNDEFINED; } + /** Get the split stage of task is under it. */ + public @StageType int getStageOfTask(int taskId) { + return mStageCoordinator.getStageOfTask(taskId); + } + /** Check split is foreground and task is under split or not by taskId. */ public boolean isTaskInSplitScreenForeground(int taskId) { return isTaskInSplitScreen(taskId) && isSplitScreenVisible(); @@ -378,17 +384,35 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); } - public void enterSplitScreen(int taskId, boolean leftOrTop) { - enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); - } - + /** + * Doing necessary window transaction for other transition handler need to enter split in + * transition. + */ public void prepareEnterSplitScreen(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo, int startPosition) { - mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition); + mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition, + false /* resizeAnim */); + } + + /** + * Doing necessary surface transaction for other transition handler need to enter split in + * transition when finished. + */ + public void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { + mStageCoordinator.finishEnterSplitScreen(finishT); } - public void finishEnterSplitScreen(SurfaceControl.Transaction t) { - mStageCoordinator.finishEnterSplitScreen(t); + /** + * Doing necessary window transaction for other transition handler need to exit split in + * transition. + */ + public void prepareExitSplitScreen(WindowContainerTransaction wct, + @StageType int stageToTop) { + mStageCoordinator.prepareExitSplitScreen(stageToTop, wct); + } + + public void enterSplitScreen(int taskId, boolean leftOrTop) { + enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); } public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 986309948ada..d21f8a48e62a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -199,19 +199,7 @@ class SplitScreenTransitions { boolean isOpening = TransitionUtil.isOpeningType(info.getType()); if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out - if (change.getSnapshot() != null) { - // This case is happened if task is going to reparent to TDA, the origin leash - // doesn't rendor so we use snapshot to replace it animating. - t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash()); - // Use origin leash layer. - t.setLayer(change.getSnapshot(), info.getChanges().size() - i); - t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left, - change.getStartAbsBounds().top); - t.show(change.getSnapshot()); - startFadeAnimation(change.getSnapshot(), false /* show */); - } else { - startFadeAnimation(leash, false /* show */); - } + startFadeAnimation(leash, false /* show */); } else if (mode == TRANSIT_CHANGE && change.getSnapshot() != null) { t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash()); // Ensure snapshot it on the top of all transition surfaces 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 b2526ee97a21..9d278b721bec 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 @@ -122,7 +122,6 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -171,7 +170,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); - private final DisplayLayout mDisplayLayout; @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -311,7 +309,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, this::onTransitionAnimationComplete, this); mDisplayController.addDisplayWindowListener(this); - mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId)); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); @@ -345,7 +342,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainExecutor = mainExecutor; mRecentTasks = recentTasks; mDisplayController.addDisplayWindowListener(this); - mDisplayLayout = new DisplayLayout(); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); @@ -393,7 +389,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, WindowContainerTransaction wct) { - prepareEnterSplitScreen(wct, task, stagePosition); + prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */); if (ENABLE_SHELL_TRANSITIONS) { mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, @@ -491,20 +487,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Launches an activity into split. */ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { + mSplitRequest = new SplitRequest(intent.getIntent(), position); if (!ENABLE_SHELL_TRANSITIONS) { startIntentLegacy(intent, fillInIntent, position, options); return; } final WindowContainerTransaction wct = new WindowContainerTransaction(); - options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); wct.sendPendingIntent(intent, fillInIntent, options); + // If this should be mixed, just send the intent to avoid split handle transition directly. + if (mMixedHandler != null && mMixedHandler.shouldSplitEnterMixed(intent)) { + mTaskOrganizer.applyTransaction(wct); + return; + } + // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; - prepareEnterSplitScreen(wct, null /* taskInfo */, position); + prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, extraTransitType, !mIsDropEntering); @@ -561,7 +563,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isEnteringSplit && mLogger.isEnterRequestedByDrag()) { updateWindowBounds(mSplitLayout, wct); } - mSplitRequest = new SplitRequest(intent.getIntent(), position); wct.sendPendingIntent(intent, fillInIntent, options); mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } @@ -579,6 +580,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.get().removeSplitPair(taskId1); } options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, null); wct.startTask(taskId1, options1); mSplitTransitions.startFullscreenTransition(wct, remoteTransition); return; @@ -600,6 +602,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, null); wct.sendPendingIntent(pendingIntent, fillInIntent, options1); mSplitTransitions.startFullscreenTransition(wct, remoteTransition); return; @@ -620,6 +623,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); if (taskId == INVALID_TASK_ID) { options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, null); wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); mSplitTransitions.startFullscreenTransition(wct, remoteTransition); return; @@ -678,6 +682,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); if (pendingIntent2 == null) { options1 = options1 != null ? options1 : new Bundle(); + addActivityOptions(options1, null); if (shortcutInfo1 != null) { wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1); } else { @@ -1445,7 +1450,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * an existing WindowContainerTransaction (rather than applying immediately). This is intended * to be used when exiting split might be bundled with other window operations. */ - private void prepareExitSplitScreen(@StageType int stageToTop, + void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { if (!mMainStage.isActive()) return; mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); @@ -1453,7 +1458,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private void prepareEnterSplitScreen(WindowContainerTransaction wct) { - prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED); + prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED, + !mIsDropEntering); } /** @@ -1461,30 +1467,37 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * into side stage. */ void prepareEnterSplitScreen(WindowContainerTransaction wct, - @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, + boolean resizeAnim) { onSplitScreenEnter(); if (isSplitActive()) { - prepareBringSplit(wct, taskInfo, startPosition); + prepareBringSplit(wct, taskInfo, startPosition, resizeAnim); } else { - prepareActiveSplit(wct, taskInfo, startPosition); + prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim); } } private void prepareBringSplit(WindowContainerTransaction wct, - @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, + boolean resizeAnim) { if (taskInfo != null) { wct.startTask(taskInfo.taskId, resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); } // If running background, we need to reparent current top visible task to main stage. if (!isSplitScreenVisible()) { + // Ensure to evict old splitting tasks because the new split pair might be composed by + // one of the splitting tasks, evicting the task when finishing entering transition + // won't guarantee to put the task to the indicated new position. + mMainStage.evictAllChildren(wct); mMainStage.reparentTopTask(wct); - prepareSplitLayout(wct); + prepareSplitLayout(wct, resizeAnim); } } private void prepareActiveSplit(WindowContainerTransaction wct, - @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { + @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, + boolean resizeAnim) { if (!ENABLE_SHELL_TRANSITIONS) { // Legacy transition we need to create divider here, shell transition case we will // create it on #finishEnterSplitScreen @@ -1495,17 +1508,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.addTask(taskInfo, wct); } mMainStage.activate(wct, true /* includingTopTask */); - prepareSplitLayout(wct); + prepareSplitLayout(wct, resizeAnim); } - private void prepareSplitLayout(WindowContainerTransaction wct) { - if (mIsDropEntering) { - mSplitLayout.resetDividerPosition(); - } else { + private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) { + if (resizeAnim) { mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); + } else { + mSplitLayout.resetDividerPosition(); } updateWindowBounds(mSplitLayout, wct); - if (!mIsDropEntering) { + if (resizeAnim) { // Reset its smallest width dp to avoid is change layout before it actually resized to // split bounds. wct.setSmallestScreenWidthDp(mMainStage.mRootTaskInfo.token, @@ -1515,21 +1528,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setRootForceTranslucent(false, wct); } - void finishEnterSplitScreen(SurfaceControl.Transaction t) { - mSplitLayout.update(t); + void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { + mSplitLayout.update(finishT); mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash, getMainStageBounds()); mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash, getSideStageBounds()); - setDividerVisibility(true, t); + setDividerVisibility(true, finishT); // Ensure divider surface are re-parented back into the hierarchy at the end of the // transition. See Transition#buildFinishTransaction for more detail. - t.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash); + finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash); - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - t.show(mRootTaskLeash); + updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); + finishT.show(mRootTaskLeash); setSplitsVisible(true); mIsDropEntering = false; + mSplitRequest = null; updateRecentTasksSplitPair(); if (!mLogger.hasStartedSession()) { mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), @@ -1622,7 +1636,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(), mSplitLayout.isLandscape()); } - if (present && visible) { + if (present) { updateRecentTasksSplitPair(); } @@ -1681,7 +1695,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSplitLayout == null) { mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mRootTaskInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, + mDisplayController, mDisplayImeController, mTaskOrganizer, PARALLAX_ALIGN_CENTER /* parallaxType */); mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); } @@ -2145,8 +2159,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (displayId != DEFAULT_DISPLAY) { return; } - mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId)); - if (mSplitLayout != null && mSplitLayout.isDensityChanged(newConfig.densityDpi) && mMainStage.isActive() && mSplitLayout.updateConfiguration(newConfig) @@ -2163,10 +2175,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { - if (!mMainStage.isActive()) return; + if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return; - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); - mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets()); + mSplitLayout.rotateTo(toRotation); if (newDisplayAreaInfo != null) { mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration); } @@ -2307,8 +2318,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.setDismissTransition(transition, dismissTop, EXIT_REASON_APP_FINISHED); } else if (!isSplitScreenVisible() && isOpening) { - // If split running backgroud and trigger task is appearing into split, - // prepare to enter split screen. + // If split is running in the background and the trigger task is appearing into + // split, prepare to enter split screen. + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); @@ -2334,6 +2346,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isOpening && getStageOfTask(triggerTask) != null) { // One task is appearing into split, prepare to enter split screen. out = new WindowContainerTransaction(); + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, out); prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), TRANSIT_SPLIT_SCREEN_PAIR_OPEN, !mIsDropEntering); @@ -2435,7 +2448,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, continue; } final StageTaskListener stage = getStageOfTask(taskInfo); - if (stage == null) continue; + if (stage == null) { + if (change.getParent() == null && !isClosingType(change.getMode()) + && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { + record.mContainShowFullscreenChange = true; + } + continue; + } if (isOpeningType(change.getMode())) { if (!stage.containsTask(taskInfo.taskId)) { Log.w(TAG, "Expected onTaskAppeared on " + stage + " to have been called" @@ -2450,22 +2469,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } } - // If the size of dismissStages == 1, one of the task is closed without prepare pending - // transition, which could happen if all activities were finished after finish top - // activity in a task, so the trigger task is null when handleRequest. - // Note if the size of dismissStages == 2, it's starting a new task, so don't handle it. final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage(); if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0 || dismissStages.size() == 1) { + // If the size of dismissStages == 1, one of the task is closed without prepare + // pending transition, which could happen if all activities were finished after + // finish top activity in a task, so the trigger task is null when handleRequest. + // Note if the size of dismissStages == 2, it's starting a new task, + // so don't handle it. Log.e(TAG, "Somehow removed the last task in a stage outside of a proper " + "transition."); final WindowContainerTransaction wct = new WindowContainerTransaction(); final int dismissTop = (dismissStages.size() == 1 && getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN) || mMainStage.getChildCount() == 0 ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; - prepareExitSplitScreen(dismissTop, wct); + // If there is a fullscreen opening change, we should not bring stage to top. + prepareExitSplitScreen(record.mContainShowFullscreenChange + ? STAGE_TYPE_UNDEFINED : dismissTop, wct); mSplitTransitions.startDismissTransition(wct, this, dismissTop, - EXIT_REASON_UNKNOWN); + EXIT_REASON_APP_FINISHED); // This can happen in some pathological cases. For example: // 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C] // 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time @@ -2473,7 +2495,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // TODO(b/184679596): Find a way to either include task-org information in // the transition, or synchronize task-org callbacks. } - // Use normal animations. return false; } else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) { @@ -2481,6 +2502,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // handling to the mixed-handler to deal with splitting it up. if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info, startTransaction, finishTransaction, finishCallback)) { + mSplitLayout.update(startTransaction); + startTransaction.apply(); return true; } } @@ -2490,6 +2513,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } static class StageChangeRecord { + boolean mContainShowFullscreenChange = false; static class StageChange { final StageTaskListener mStageTaskListener; final IntArray mAddedTaskId = new IntArray(); @@ -2622,6 +2646,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, Log.w(TAG, "Launched a task in split, but didn't receive any task in transition."); mSplitTransitions.mPendingEnter.cancel((cancelWct, cancelT) -> prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, cancelWct)); + mSplitUnsupportedToast.show(); return true; } } else { @@ -2632,6 +2657,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); mSplitTransitions.mPendingEnter.cancel( (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct)); + mSplitUnsupportedToast.show(); return true; } } @@ -2862,6 +2888,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** Call this when the recents animation canceled during split-screen. */ public void onRecentsInSplitAnimationCanceled() { mPausingTasks.clear(); + setSplitsVisible(false); + + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, + true /* reparentLeafTaskIfRelaunch */); + mTaskOrganizer.applyTransaction(wct); } /** Call this when the recents animation during split-screen finishes. */ @@ -2886,8 +2918,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } setSplitsVisible(false); - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, finishWct); - logExit(EXIT_REASON_UNKNOWN); + finishWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, + true /* reparentLeafTaskIfRelaunch */); } /** Call this when the recents animation finishes by doing pair-to-pair switch. */ 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 92ff5fed4584..a01eddbc9b9f 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 @@ -127,7 +127,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { * Returns the top visible child task's id. */ int getTopVisibleChildTaskId() { - final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible); + final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible + && t.isVisibleRequested); return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID; } @@ -183,12 +184,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); mChildrenTaskInfo.put(taskId, taskInfo); - updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); - mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible); + mCallbacks.onChildTaskStatusChanged(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 { @@ -223,7 +225,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, - taskInfo.isVisible); + taskInfo.isVisible && taskInfo.isVisibleRequested); if (!ENABLE_SHELL_TRANSITIONS) { updateChildTaskSurface( taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); @@ -417,7 +419,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } t.setCrop(leash, null); t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); - if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) { + if (firstAppeared) { t.setAlpha(leash, 1f); t.setMatrix(leash, 1, 0, 0, 1); t.show(leash); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java index 1bbd3679948b..163cf501734c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java @@ -61,6 +61,16 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { private TaskViewBase mTaskViewBase; private final Context mContext; + /** + * There could be a situation where we have task info and receive + * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}, however, the + * activity might fail to open, and in this case we need to clean up the task view / notify + * listeners of a task removal. This requires task info, so we save the info from onTaskAppeared + * in this situation to allow us to notify listeners correctly if the task failed to open. + */ + private ActivityManager.RunningTaskInfo mPendingInfo; + /* Indicates that the task we attempted to launch in the task view failed to launch. */ + private boolean mTaskNotFound; protected ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; private SurfaceControl mTaskLeash; @@ -236,6 +246,8 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { mTaskInfo = null; mTaskToken = null; mTaskLeash = null; + mPendingInfo = null; + mTaskNotFound = false; } private void updateTaskVisibility() { @@ -257,6 +269,12 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (isUsingShellTransitions()) { + mPendingInfo = taskInfo; + if (mTaskNotFound) { + // If we were already notified by shell transit that we don't have the + // the task, clean it up now. + cleanUpPendingTask(); + } // Everything else handled by enter transition. return; } @@ -455,6 +473,42 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { return mTaskInfo; } + /** + * Indicates that the task was not found in the start animation for the transition. + * In this case we should clean up the task if we have the pending info. If we don't + * have the pending info, we'll do it when we receive it in + * {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)}. + */ + void setTaskNotFound() { + mTaskNotFound = true; + if (mPendingInfo != null) { + cleanUpPendingTask(); + } + } + + /** + * Called when a task failed to open and we need to clean up task view / + * notify users of task view. + */ + void cleanUpPendingTask() { + if (mPendingInfo != null) { + if (mListener != null) { + final int taskId = mPendingInfo.taskId; + mListenerExecutor.execute(() -> { + mListener.onTaskRemovalStarted(taskId); + }); + } + mTaskViewBase.onTaskVanished(mPendingInfo); + mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mPendingInfo.token, false); + + // Make sure the task is removed + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.removeTask(mPendingInfo.token); + mTaskViewTransitions.closeTaskView(wct, this); + } + resetTaskInfo(); + } + void prepareHideAnimation(@NonNull SurfaceControl.Transaction finishTransaction) { if (mTaskToken == null) { // Nothing to update, task is not yet available @@ -492,6 +546,7 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener { @NonNull SurfaceControl.Transaction finishTransaction, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash, WindowContainerTransaction wct) { + mPendingInfo = null; mTaskInfo = taskInfo; mTaskToken = mTaskInfo.token; mTaskLeash = leash; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java index 2e7fca3f2b46..5baf2e320227 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java @@ -139,7 +139,8 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { * `taskView`. * @param taskView the pending transition should be for this. */ - private PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) { + @VisibleForTesting + PendingTransition findPendingOpeningTransition(TaskViewTaskController taskView) { for (int i = mPending.size() - 1; i >= 0; --i) { if (mPending.get(i).mTaskView != taskView) continue; if (TransitionUtil.isOpeningType(mPending.get(i).mType)) { @@ -398,10 +399,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler { } } if (stillNeedsMatchingLaunch) { - throw new IllegalStateException("Expected a TaskView launch in this transition but" - + " didn't get one."); - } - if (wct == null && pending == null && changesHandled != info.getChanges().size()) { + Slog.w(TAG, "Expected a TaskView launch in this transition but didn't get one, " + + "cleaning up the task view"); + // Didn't find a task so the task must have never launched + pending.mTaskView.setTaskNotFound(); + } else if (wct == null && pending == null && changesHandled != info.getChanges().size()) { // Just some house-keeping, let another handler animate. return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 64571e067a83..2aa9f20df746 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -24,13 +24,16 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; import static com.android.wm.shell.util.TransitionUtil.isOpeningType; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.PendingIntent; import android.os.IBinder; +import android.util.Log; import android.util.Pair; import android.view.SurfaceControl; import android.view.WindowManager; @@ -40,6 +43,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.phone.PipTouchHandler; @@ -157,7 +161,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) { + if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive() + && request.getTriggerTask() != null && mSplitHandler.getSplitItemPosition( + request.getTriggerTask().token) != SPLIT_POSITION_UNDEFINED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while " + "Split-Screen is active, so treat it as Mixed."); if (request.getRemoteTransition() != null) { @@ -566,11 +572,18 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - boolean consumed = mKeyguardHandler.startAnimation( - mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); - if (!consumed) { + final Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + mixed.mInFlightSubAnimations--; + if (mixed.mInFlightSubAnimations == 0) { + mActiveTransitions.remove(mixed); + finishCallback.onTransitionFinished(wct, wctCB); + } + }; + if (!mKeyguardHandler.startAnimation( + mixed.mTransition, info, startTransaction, finishTransaction, finishCB)) { return false; } + mixed.mInFlightSubAnimations++; // Sync pip state. if (mPipHandler != null) { // We don't know when to apply `startTransaction` so use a separate transaction here. @@ -582,6 +595,17 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return true; } + /** Use to when split use intent to enter, check if this enter transition should be mixed or + * not.*/ + public boolean shouldSplitEnterMixed(PendingIntent intent) { + // Check if this intent package is same as pip one or not, if true we want let the pip + // task enter split. + if (mPipHandler != null) { + return mPipHandler.isInPipPackage(SplitScreenUtils.getPackageName(intent.getIntent())); + } + return false; + } + @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, 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 4c678a2bf29b..e52fd00e7df7 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 @@ -58,7 +58,6 @@ 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.TransitionAnimationHelper.addBackgroundToTransition; 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.loadAttributeAnimation; @@ -76,6 +75,7 @@ import android.content.BroadcastReceiver; 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; @@ -103,6 +103,7 @@ import com.android.internal.policy.AttributeCache; import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; @@ -137,6 +138,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private final Rect mInsets = new Rect(0, 0, 0, 0); private float mTransitionAnimationScaleSetting = 1.0f; + private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final int mCurrentUserId; private Drawable mEnterpriseThumbnailDrawable; @@ -157,7 +159,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @NonNull DisplayController displayController, @NonNull TransactionPool transactionPool, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, - @NonNull ShellExecutor animExecutor) { + @NonNull ShellExecutor animExecutor, + @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mDisplayController = displayController; mTransactionPool = transactionPool; mContext = context; @@ -168,6 +171,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { mCurrentUserId = UserHandle.myUserId(); mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); shellInit.addInitCallback(this::onInit, this); + mRootTDAOrganizer = rootTDAOrganizer; } private void onInit() { @@ -256,6 +260,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // This is the only way to get display-id currently, so check display capabilities here. final DisplayLayout displayLayout = displayController.getDisplayLayout( topTaskInfo.displayId); + // This condition should be true when using gesture navigation or the screen size is large + // (>600dp) because the bar is small relative to screen. + if (displayLayout.allowSeamlessRotationDespiteNavBarMoving()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " nav bar allows seamless."); + return ROTATION_ANIMATION_SEAMLESS; + } // For the upside down rotation we don't rotate seamlessly as the navigation bar moves // position. Note most apps (using orientation:sensor or user as opposed to fullSensor) // will not enter the reverse portrait orientation, so actually the orientation won't @@ -268,13 +278,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return animationHint; } - // If the navigation bar can't change sides, then it will jump when we change orientations - // and we don't rotate seamlessly - unless that is allowed, e.g. with gesture navigation - // where the navbar is low-profile enough that this isn't very noticeable. - if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving() - && (!(displayLayout.navigationBarCanMove() - && (displayChange.getStartAbsBounds().width() - != displayChange.getStartAbsBounds().height())))) { + // If the navigation bar cannot change sides, then it will jump when changing orientation + // so do not use seamless rotation. + if (!displayLayout.navigationBarCanMove()) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " nav bar changes sides, so not seamless."); return animationHint; @@ -510,10 +516,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } if (backgroundColorForTransition != 0) { - for (int i = 0; i < info.getRootCount(); ++i) { - addBackgroundToTransition(info.getRoot(i).getLeash(), backgroundColorForTransition, - startTransaction, finishTransaction); - } + addBackgroundColorOnTDA(info, backgroundColorForTransition, startTransaction, + finishTransaction); } if (postStartTransactionCallbacks.size() > 0) { @@ -543,6 +547,28 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return true; } + private void addBackgroundColorOnTDA(@NonNull TransitionInfo info, + @ColorInt int color, @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + final Color bgColor = Color.valueOf(color); + final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() }; + + for (int i = 0; i < info.getRootCount(); ++i) { + final int displayId = info.getRoot(i).getDisplayId(); + final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder() + .setName("animation-background") + .setCallsite("DefaultTransitionHandler") + .setColorLayer(); + + mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder); + final SurfaceControl backgroundSurface = colorLayerBuilder.build(); + startTransaction.setColor(backgroundSurface, colorArray) + .setLayer(backgroundSurface, -1) + .show(backgroundSurface); + finishTransaction.remove(backgroundSurface); + } + } + private static boolean isDreamTransition(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -696,9 +722,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { if (a != null) { if (!a.isInitialized()) { - final int width = endBounds.width(); - final int height = endBounds.height(); - a.initialize(width, height, width, height); + final Rect animationRange = TransitionUtil.isClosingType(changeMode) + ? change.getStartAbsBounds() : change.getEndAbsBounds(); + a.initialize(animationRange.width(), animationRange.height(), + endBounds.width(), endBounds.height()); } a.restrictDuration(MAX_ANIMATION_DURATION); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); @@ -873,18 +900,31 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } + /** + * Returns {@code true} if the default transition handler can run the override animation. + * @see #loadAnimation(TransitionInfo, TransitionInfo.Change, int, boolean) + */ + public static boolean isSupportedOverrideAnimation( + @NonNull TransitionInfo.AnimationOptions options) { + final int animType = options.getType(); + return animType == ANIM_CUSTOM || animType == ANIM_SCALE_UP + || animType == ANIM_THUMBNAIL_SCALE_UP || animType == ANIM_THUMBNAIL_SCALE_DOWN + || animType == ANIM_CLIP_REVEAL || animType == ANIM_OPEN_CROSS_PROFILE_APPS; + } + private static void applyTransformation(long time, SurfaceControl.Transaction t, - SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix, + SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix, Point position, float cornerRadius, @Nullable Rect immutableClipRect) { - anim.getTransformation(time, transformation); + tmpTransformation.clear(); + anim.getTransformation(time, tmpTransformation); if (position != null) { - transformation.getMatrix().postTranslate(position.x, position.y); + tmpTransformation.getMatrix().postTranslate(position.x, position.y); } - t.setMatrix(leash, transformation.getMatrix(), matrix); - t.setAlpha(leash, transformation.getAlpha()); + t.setMatrix(leash, tmpTransformation.getMatrix(), matrix); + t.setAlpha(leash, tmpTransformation.getAlpha()); final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect); - Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE); + 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); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index ce8d792ef302..cdc82eadcd90 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -29,6 +29,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.fixScale; import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; +import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_NO_ANIMATION; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -68,6 +69,7 @@ import androidx.annotation.BinderThread; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; @@ -264,7 +266,8 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor) { this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor, - mainHandler, animExecutor, null); + mainHandler, animExecutor, null, + new RootTaskDisplayAreaOrganizer(mainExecutor, context)); } public Transitions(@NonNull Context context, @@ -276,7 +279,8 @@ public class Transitions implements RemoteCallable<Transitions>, @NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler, @NonNull ShellExecutor animExecutor, - @Nullable ShellCommandHandler shellCommandHandler) { + @Nullable ShellCommandHandler shellCommandHandler, + @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { mOrganizer = organizer; mContext = context; mMainExecutor = mainExecutor; @@ -284,7 +288,7 @@ public class Transitions implements RemoteCallable<Transitions>, mDisplayController = displayController; mPlayerImpl = new TransitionPlayerImpl(); mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit, - displayController, pool, mainExecutor, mainHandler, animExecutor); + displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer); mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor); mShellController = shellController; // The very last handler (0 in the list) should be the default one. @@ -554,7 +558,10 @@ public class Transitions implements RemoteCallable<Transitions>, layer = -zSplitLine - i; } } else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { - if (isOpening) { + if (isOpening + // This is for when an activity launches while a different transition is + // collecting. + || change.hasFlags(FLAG_MOVED_TO_TOP)) { // put on top layer = zSplitLine + numChanges - i; } else { @@ -637,6 +644,7 @@ public class Transitions implements RemoteCallable<Transitions>, @VisibleForTesting void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { + info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady"); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s", transitionToken, info); final int activeIdx = findByToken(mPendingTransitions, transitionToken); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index bb0eba6a0fc7..c504f57216f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -173,6 +173,17 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene mTransition = null; } + @Override + public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull TransitionFinishCallback finishCallback) { + if (info.getType() == TRANSIT_CHANGE) { + // Apply changes happening during the unfold animation immediately + t.apply(); + finishCallback.onTransitionFinished(null, null); + } + } + @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java index 143b42a1ec96..c33a633f2068 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java @@ -121,15 +121,14 @@ public class TransitionUtil { @Override public boolean test(TransitionInfo.Change change) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + if (taskInfo == null) return false; // Children always come before parent since changes are in top-to-bottom z-order. - if ((taskInfo == null) || mChildTaskTargets.get(taskInfo.taskId)) { - // has children, so not a leaf. Skip. - return false; - } + boolean hasChildren = mChildTaskTargets.get(taskInfo.taskId); if (taskInfo.hasParentTask()) { mChildTaskTargets.put(taskInfo.parentTaskId, true); } - return true; + // If it has children, it's not a leaf. + return !hasChildren; } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt new file mode 100644 index 000000000000..f7ce87088040 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.pip + +import android.app.Instrumentation +import android.os.SystemClock +import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils +import android.tools.device.traces.parsers.toFlickerComponent +import androidx.test.filters.RequiresDevice +import androidx.test.uiautomator.By +import androidx.test.uiautomator.BySelector +import androidx.test.uiautomator.UiObject2 +import androidx.test.uiautomator.Until +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.testapp.ActivityOptions +import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app via auto-enter property when navigating to home from split screen. + * + * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest` + * + * Actions: + * ``` + * Launch an app in full screen + * Select "Auto-enter PiP" radio button + * Open all apps and drag another app icon to enter split screen + * Press Home button or swipe up to go Home and put [pipApp] in pip mode + * ``` + * + * Notes: + * ``` + * 1. All assertions are inherited from [EnterPipTest] + * 2. Part of the test setup occurs automatically via + * [android.tools.device.flicker.legacy.runner.TransitionRunner], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + * ``` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) : + AutoEnterPipOnGoToHomeTest(flicker) { + private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) + /** Second app used to enter split screen mode */ + protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation) + fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper = + SimpleAppHelper( + instrumentation, + ActivityOptions.SplitScreen.Primary.LABEL, + ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent() + ) + + /** Defines the transition used to run the test */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + secondAppForSplitScreen.launchViaIntent(wmHelper) + pipApp.launchViaIntent(wmHelper) + tapl.goHome() + enterSplitScreen() + // wait until split screen is established + wmHelper + .StateSyncBuilder() + .withWindowSurfaceAppeared(pipApp) + .withWindowSurfaceAppeared(secondAppForSplitScreen) + .withSplitDividerVisible() + .waitForAndVerify() + pipApp.enableAutoEnterForPipActivity() + } + teardown { + // close gracefully so that onActivityUnpinned() can be called before force exit + pipApp.closePipWindow(wmHelper) + pipApp.exit(wmHelper) + secondAppForSplitScreen.exit(wmHelper) + } + transitions { tapl.goHome() } + } + + // TODO(b/285400227) merge the code in a common utility - this is copied from SplitScreenUtils + private val TIMEOUT_MS = 3_000L + private val overviewSnapshotSelector: BySelector + get() = By.res(LAUNCHER_UI_PACKAGE_NAME, "snapshot") + private fun enterSplitScreen() { + // Note: The initial split position in landscape is different between tablet and phone. + // In landscape, tablet will let the first app split to right side, and phone will + // split to left side. + if (tapl.isTablet) { + // TAPL's currentTask on tablet is sometimes not what we expected if the overview + // contains more than 3 task views. We need to use uiautomator directly to find the + // second task to split. + tapl.workspace.switchToOverview().overviewActions.clickSplit() + val snapshots = tapl.device.wait(Until.findObjects(overviewSnapshotSelector), + TIMEOUT_MS) + if (snapshots == null || snapshots.size < 1) { + error("Fail to find a overview snapshot to split.") + } + + // Find the second task in the upper right corner in split select mode by sorting + // 'left' in descending order and 'top' in ascending order. + snapshots.sortWith { t1: UiObject2, t2: UiObject2 -> + t2.getVisibleBounds().left - t1.getVisibleBounds().left + } + snapshots.sortWith { t1: UiObject2, t2: UiObject2 -> + t1.getVisibleBounds().top - t2.getVisibleBounds().top + } + snapshots[0].click() + } else { + tapl.workspace + .switchToOverview() + .currentTask + .tapMenu() + .tapSplitMenuItem() + .currentTask + .open() + } + SystemClock.sleep(TIMEOUT_MS) + } + + @Presubmit + @Test + override fun pipOverlayLayerAppearThenDisappear() { + // when entering from split screen we use alpha animation, without overlay + } + + @Presubmit + @Test + override fun pipLayerOrOverlayRemainInsideVisibleBounds() { + // when entering from split screen we use alpha animation, without overlay + } + + @Presubmit + @Test + override fun pipLayerReduces() { + // when entering from split screen we use alpha animation, so there is no size change + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipLayerReduces() + } + + @Presubmit + @Test + override fun pipAppLayerAlwaysVisible() { + // pip layer in gesture nav will disappear during transition with alpha animation + Assume.assumeFalse(flicker.scenario.isGesturalNavigation) + super.pipAppLayerAlwaysVisible() + } + + @Presubmit + @Test + override fun pipWindowRemainInsideVisibleBounds() { + if (tapl.isTablet) { + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } + } else { + // on phones home does not rotate in landscape, PiP enters back to portrait + // orientation so use display bounds from that orientation for assertion + flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index a4ac261d1946..b95732e43357 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder @@ -54,21 +53,25 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) { - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - pipApp.launchViaIntent(wmHelper) - pipApp.enableAutoEnterForPipActivity() - } - teardown { - // close gracefully so that onActivityUnpinned() can be called before force exit - pipApp.closePipWindow(wmHelper) - pipApp.exit(wmHelper) - } - transitions { tapl.goHome() } +open class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) { + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { tapl.goHome() } + } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + pipApp.launchViaIntent(wmHelper) + pipApp.enableAutoEnterForPipActivity() } + } + + override val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + // close gracefully so that onActivityUnpinned() can be called before force exit + pipApp.closePipWindow(wmHelper) + pipApp.exit(wmHelper) + } + } @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt index 98fc91b334cf..afcc1729ed16 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt @@ -54,40 +54,38 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class ClosePipBySwipingDownTest(flicker: FlickerTest) : ClosePipTransition(flicker) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - val pipRegion = wmHelper.getWindowRegion(pipApp).bounds - val pipCenterX = pipRegion.centerX() - val pipCenterY = pipRegion.centerY() - val displayCenterX = device.displayWidth / 2 - val barComponent = - if (flicker.scenario.isTablet) { - ComponentNameMatcher.TASK_BAR - } else { - ComponentNameMatcher.NAV_BAR - } - val barLayerHeight = - wmHelper.currentState.layerState - .getLayerWithBuffer(barComponent) - ?.visibleRegion - ?.height - ?: error("Couldn't find Nav or Task bar layer") - // The dismiss button doesn't appear at the complete bottom of the screen, - // it appears above the hot seat but `hotseatBarSize` is not available outside - // the platform - val displayY = (device.displayHeight * 0.9).toInt() - barLayerHeight - device.swipe(pipCenterX, pipCenterY, displayCenterX, displayY, 50) - // Wait until the other app is no longer visible - wmHelper - .StateSyncBuilder() - .withPipGone() - .withWindowSurfaceDisappeared(pipApp) - .withAppTransitionIdle() - .waitForAndVerify() - } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { + val pipRegion = wmHelper.getWindowRegion(pipApp).bounds + val pipCenterX = pipRegion.centerX() + val pipCenterY = pipRegion.centerY() + val displayCenterX = device.displayWidth / 2 + val barComponent = + if (flicker.scenario.isTablet) { + ComponentNameMatcher.TASK_BAR + } else { + ComponentNameMatcher.NAV_BAR + } + val barLayerHeight = + wmHelper.currentState.layerState + .getLayerWithBuffer(barComponent) + ?.visibleRegion + ?.height + ?: error("Couldn't find Nav or Task bar layer") + // The dismiss button doesn't appear at the complete bottom of the screen, + // it appears above the hot seat but `hotseatBarSize` is not available outside + // the platform + val displayY = (device.displayHeight * 0.9).toInt() - barLayerHeight + device.swipe(pipCenterX, pipCenterY, displayCenterX, displayY, 50) + // Wait until the other app is no longer visible + wmHelper + .StateSyncBuilder() + .withPipGone() + .withWindowSurfaceDisappeared(pipApp) + .withAppTransitionIdle() + .waitForAndVerify() } + } /** Checks that the focus doesn't change between windows during the transition */ @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt index 2417c45bf9a0..e52b71e602f9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt @@ -28,11 +28,10 @@ import org.junit.runners.Parameterized /** Base class for exiting pip (closing pip window) without returning to the app */ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) { - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { - setup { this.setRotation(flicker.scenario.startRotation) } - teardown { this.setRotation(Rotation.ROTATION_0) } - } + override val thisTransition: FlickerBuilder.() -> Unit = { + setup { this.setRotation(flicker.scenario.startRotation) } + teardown { this.setRotation(Rotation.ROTATION_0) } + } /** * Checks that [pipApp] window is pinned and visible at the start and then becomes unpinned and diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt index d16583271e8c..86fe583c94e6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt @@ -54,12 +54,9 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class ClosePipWithDismissButtonTest(flicker: FlickerTest) : ClosePipTransition(flicker) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { pipApp.closePipWindow(wmHelper) } - } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.closePipWindow(wmHelper) } + } /** * Checks that the focus changes between the pip menu window and the launcher when clicking the diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index f52e877ec2b1..01d67cc35a14 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -45,20 +45,24 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class EnterPipOnUserLeaveHintTest(flicker: FlickerTest) : EnterPipTransition(flicker) { - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - pipApp.launchViaIntent(wmHelper) - pipApp.enableEnterPipOnUserLeaveHint() - } - teardown { - // close gracefully so that onActivityUnpinned() can be called before force exit - pipApp.closePipWindow(wmHelper) - pipApp.exit(wmHelper) - } - transitions { tapl.goHome() } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { tapl.goHome() } + } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + pipApp.launchViaIntent(wmHelper) + pipApp.enableEnterPipOnUserLeaveHint() + } + } + + override val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + // close gracefully so that onActivityUnpinned() can be called before force exit + pipApp.closePipWindow(wmHelper) + pipApp.exit(wmHelper) } + } @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index 4b4613704a16..5480144ba1ce 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -73,39 +73,39 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - // Launch a portrait only app on the fullscreen stack - testApp.launchViaIntent( + override val thisTransition: FlickerBuilder.() -> Unit = { + teardown { + testApp.exit(wmHelper) + } + transitions { + // Enter PiP, and assert that the PiP is within bounds now that the device is back + // in portrait + broadcastActionTrigger.doAction(ACTION_ENTER_PIP) + // during rotation the status bar becomes invisible and reappears at the end + wmHelper + .StateSyncBuilder() + .withPipShown() + .withNavOrTaskBarVisible() + .withStatusBarVisible() + .waitForAndVerify() + } + } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + // Launch a portrait only app on the fullscreen stack + testApp.launchViaIntent( wmHelper, stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString()) - ) - // Launch the PiP activity fixed as landscape - pipApp.launchViaIntent( + ) + // Launch the PiP activity fixed as landscape, but don't enter PiP + pipApp.launchViaIntent( wmHelper, stringExtras = - mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) - ) - } - teardown { - pipApp.exit(wmHelper) - testApp.exit(wmHelper) - } - transitions { - // Enter PiP, and assert that the PiP is within bounds now that the device is back - // in portrait - broadcastActionTrigger.doAction(ACTION_ENTER_PIP) - // during rotation the status bar becomes invisible and reappears at the end - wmHelper - .StateSyncBuilder() - .withPipShown() - .withNavOrTaskBarVisible() - .withStatusBarVisible() - .waitForAndVerify() - } + mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) + ) } + } /** * This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt index bfd57786e615..cdbdb85a9195 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt @@ -26,12 +26,11 @@ import org.junit.Test import org.junit.runners.Parameterized abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) { - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { pipApp.launchViaIntent(wmHelper) } - teardown { pipApp.exit(wmHelper) } + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + pipApp.launchViaIntent(wmHelper) } + } /** Checks [pipApp] window remains visible throughout the animation */ @Presubmit @@ -63,7 +62,7 @@ abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) */ @Presubmit @Test - fun pipWindowRemainInsideVisibleBounds() { + open fun pipWindowRemainInsideVisibleBounds() { flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt index f1925d8c9d85..95725b64a48a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt @@ -51,11 +51,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class EnterPipViaAppUiButtonTest(flicker: FlickerTest) : EnterPipTransition(flicker) { - - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { pipApp.clickEnterPipButton(wmHelper) } - } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.clickEnterPipButton(wmHelper) } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt index 3e0e37dfc997..0b3d16a8087d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt @@ -53,19 +53,16 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class ExitPipToAppViaExpandButtonTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) { - - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { - setup { - // launch an app behind the pip one - testApp.launchViaIntent(wmHelper) - } - transitions { - // This will bring PipApp to fullscreen - pipApp.expandPipWindowToApp(wmHelper) - // Wait until the other app is no longer visible - wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify() - } + override val thisTransition: FlickerBuilder.() -> Unit = { + setup { + // launch an app behind the pip one + testApp.launchViaIntent(wmHelper) + } + transitions { + // This will bring PipApp to fullscreen + pipApp.expandPipWindowToApp(wmHelper) + // Wait until the other app is no longer visible + wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify() } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt index 603f99541a12..bb2d40becdc9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt @@ -52,19 +52,16 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class ExitPipToAppViaIntentTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) { - - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { - setup { - // launch an app behind the pip one - testApp.launchViaIntent(wmHelper) - } - transitions { - // This will bring PipApp to fullscreen - pipApp.exitPipToFullScreenViaIntent(wmHelper) - // Wait until the other app is no longer visible - wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify() - } + override val thisTransition: FlickerBuilder.() -> Unit = { + setup { + // launch an app behind the pip one + testApp.launchViaIntent(wmHelper) + } + transitions { + // This will bring PipApp to fullscreen + pipApp.exitPipToFullScreenViaIntent(wmHelper) + // Wait until the other app is no longer visible + wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify() } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt index bbb1c6c2ac63..fd16b6ea6ada 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt @@ -56,8 +56,9 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flicker) { - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { transitions { pipApp.doubleClickPipWindow(wmHelper) } } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.doubleClickPipWindow(wmHelper) } + } /** * Checks that the pip app window remains inside the display bounds throughout the whole diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt index d860e00fbfff..253aa4cae5c7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt @@ -35,8 +35,9 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicker) { - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) } } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) } + } /** Checks that the visible region area of [pipApp] always increases during the animation. */ @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt index d8d57d219933..094060f86691 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt @@ -56,15 +56,10 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) class MovePipDownOnShelfHeightChange(flicker: FlickerTest) : MovePipShelfHeightTransition(flicker) { - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { - teardown { - tapl.pressHome() - testApp.exit(wmHelper) - } - transitions { testApp.launchViaIntent(wmHelper) } - } + override val thisTransition: FlickerBuilder.() -> Unit = { + teardown { testApp.exit(wmHelper) } + transitions { testApp.launchViaIntent(wmHelper) } + } /** Checks that the visible region of [pipApp] window always moves down during the animation. */ @Presubmit @Test fun pipWindowMovesDown() = pipWindowMoves(Direction.DOWN) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt index 6b061bbb1565..ff51c27bf116 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt @@ -41,23 +41,21 @@ import org.junit.runners.Parameterized open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransition(flicker) { private val imeApp = ImeAppHelper(instrumentation) - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { - setup { - imeApp.launchViaIntent(wmHelper) - setRotation(flicker.scenario.startRotation) - } - teardown { imeApp.exit(wmHelper) } - transitions { - // open the soft keyboard - imeApp.openIME(wmHelper) - createTag(TAG_IME_VISIBLE) + override val thisTransition: FlickerBuilder.() -> Unit = { + setup { + imeApp.launchViaIntent(wmHelper) + setRotation(flicker.scenario.startRotation) + } + teardown { imeApp.exit(wmHelper) } + transitions { + // open the soft keyboard + imeApp.openIME(wmHelper) + createTag(TAG_IME_VISIBLE) - // then close it again - imeApp.closeIME(wmHelper) - } + // then close it again + imeApp.closeIME(wmHelper) } + } /** Ensure the pip window remains visible throughout any keyboard interactions */ @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt index ae3f87967658..27b061b67a85 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt @@ -57,14 +57,12 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class MovePipUpOnShelfHeightChangeTest(flicker: FlickerTest) : MovePipShelfHeightTransition(flicker) { - /** Defines the transition used to run the test */ - override val transition: FlickerBuilder.() -> Unit - get() = - buildTransition() { - setup { testApp.launchViaIntent(wmHelper) } - transitions { tapl.pressHome() } - teardown { testApp.exit(wmHelper) } - } + override val thisTransition: FlickerBuilder.() -> Unit = + { + setup { testApp.launchViaIntent(wmHelper) } + transitions { tapl.pressHome() } + teardown { testApp.exit(wmHelper) } + } /** Checks that the visible region of [pipApp] window always moves up during the animation. */ @Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt index 4e2a4e700698..9f81ba8eee87 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import android.tools.device.flicker.legacy.FlickerTestFactory -import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import com.android.server.wm.flicker.testapp.ActivityOptions import org.junit.FixMethodOrder import org.junit.Test @@ -37,28 +36,31 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) { private var isDraggedLeft: Boolean = true - override val transition: FlickerBuilder.() -> Unit - get() = { - val stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true") - setup { - tapl.setEnableRotation(true) - // Launch the PIP activity and wait for it to enter PiP mode - RemoveAllTasksButHomeRule.removeAllTasksButHome() - pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) } + } - // determine the direction of dragging to test for - isDraggedLeft = pipApp.isCloserToRightEdge(wmHelper) - } - teardown { - // release the primary pointer after dragging without release - pipApp.releasePipAfterDragging() + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + val stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true") + setup { + tapl.setEnableRotation(true) + pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) - pipApp.exit(wmHelper) - tapl.setEnableRotation(false) - } - transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) } + // determine the direction of dragging to test for + isDraggedLeft = pipApp.isCloserToRightEdge(wmHelper) + } + } + + override val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + // release the primary pointer after dragging without release + pipApp.releasePipAfterDragging() + + pipApp.exit(wmHelper) + tapl.setEnableRotation(false) } + } @Postsubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt index 8eb41b4ac694..60bf5ffdc7af 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt @@ -37,8 +37,9 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @FlakyTest(bugId = 270677470) class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) { - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) } } + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) } + } /** Checks that the visible region area of [pipApp] always decreases during the animation. */ @Postsubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index eb1245b9ab86..17a178f78de3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -56,27 +56,37 @@ abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) { } } - /** - * Gets a configuration that handles basic setup and teardown of pip tests and that launches the - * Pip app for test - * - * @param stringExtras Arguments to pass to the PIP launch intent - * @param extraSpec Additional segment of flicker specification - */ - @JvmOverloads - protected open fun buildTransition( - stringExtras: Map<String, String> = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"), - extraSpec: FlickerBuilder.() -> Unit = {} - ): FlickerBuilder.() -> Unit { - return { - setup { - setRotation(Rotation.ROTATION_0) - removeAllTasksButHome() - pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) - } - teardown { pipApp.exit(wmHelper) } + /** Defines the transition used to run the test */ + protected open val thisTransition: FlickerBuilder.() -> Unit = {} - extraSpec(this) + override val transition: FlickerBuilder.() -> Unit + get() = { + defaultSetup(this) + defaultEnterPip(this) + thisTransition(this) + defaultTeardown(this) + } + + /** Defines the default setup steps required by the test */ + protected open val defaultSetup: FlickerBuilder.() -> Unit = { + setup { + setRotation(Rotation.ROTATION_0) + removeAllTasksButHome() + } + } + + /** Defines the default method of entering PiP */ + protected open val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + pipApp.launchViaIntentAndWaitForPip(wmHelper, + stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")) + } + } + + /** Defines the default teardown required to clean up after the test */ + protected open val defaultTeardown: FlickerBuilder.() -> Unit = { + teardown { + pipApp.exit(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index 3850c1f6c89a..c618e5a24fdf 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt @@ -50,41 +50,41 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) - /** {@inheritDoc} */ - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - // Launch the PiP activity fixed as landscape. - pipApp.launchViaIntent( + override val thisTransition: FlickerBuilder.() -> Unit = { + transitions { + // Launch the activity back into fullscreen and ensure that it is now in landscape + pipApp.launchViaIntent(wmHelper) + // System bar may fade out during fixed rotation. + wmHelper + .StateSyncBuilder() + .withFullScreenApp(pipApp) + .withRotation(Rotation.ROTATION_90) + .withNavOrTaskBarVisible() + .withStatusBarVisible() + .waitForAndVerify() + } + } + + override val defaultEnterPip: FlickerBuilder.() -> Unit = { + setup { + // Launch the PiP activity fixed as landscape. + pipApp.launchViaIntent( wmHelper, stringExtras = - mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) - ) - // Enter PiP. - broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP) - // System bar may fade out during fixed rotation. - wmHelper + mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()) + ) + // Enter PiP. + broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP) + // System bar may fade out during fixed rotation. + wmHelper .StateSyncBuilder() .withPipShown() .withRotation(Rotation.ROTATION_0) .withNavOrTaskBarVisible() .withStatusBarVisible() .waitForAndVerify() - } - teardown { pipApp.exit(wmHelper) } - transitions { - // Launch the activity back into fullscreen and ensure that it is now in landscape - pipApp.launchViaIntent(wmHelper) - // System bar may fade out during fixed rotation. - wmHelper - .StateSyncBuilder() - .withFullScreenApp(pipApp) - .withRotation(Rotation.ROTATION_90) - .withNavOrTaskBarVisible() - .withStatusBarVisible() - .waitForAndVerify() - } } + } /** * This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt index 703784dd8c67..43d6c8f26126 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt @@ -63,14 +63,13 @@ open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) - override val transition: FlickerBuilder.() -> Unit - get() = buildTransition { - setup { - testApp.launchViaIntent(wmHelper) - setRotation(flicker.scenario.startRotation) - } - transitions { setRotation(flicker.scenario.endRotation) } + override val thisTransition: FlickerBuilder.() -> Unit = { + setup { + testApp.launchViaIntent(wmHelper) + setRotation(flicker.scenario.startRotation) } + transitions { setRotation(flicker.scenario.endRotation) } + } /** Checks that [testApp] layer is within [screenBoundsStart] at the start of the transition */ @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index e0dbfa3853cc..676c150815ad 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Postsubmit import android.tools.common.NavBar -import android.tools.common.Rotation import android.tools.common.flicker.subject.region.RegionSubject import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder @@ -105,8 +104,6 @@ class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) : @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - // TODO(b/283963801) address entireScreenCovered test faliure in landscape. - supportedRotations = listOf(Rotation.ROTATION_0), supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index 3d779481d361..443cea245a4f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager; @@ -41,6 +42,7 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; +import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; @@ -57,6 +59,7 @@ import org.mockito.MockitoAnnotations; public class SplitLayoutTests extends ShellTestCase { @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler; @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks; + @Mock DisplayController mDisplayController; @Mock DisplayImeController mDisplayImeController; @Mock ShellTaskOrganizer mTaskOrganizer; @Mock WindowContainerTransaction mWct; @@ -72,6 +75,7 @@ public class SplitLayoutTests extends ShellTestCase { getConfiguration(), mSplitLayoutHandler, mCallbacks, + mDisplayController, mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_NONE)); @@ -100,6 +104,10 @@ public class SplitLayoutTests extends ShellTestCase { // Verify updateConfiguration returns true if the density changed. config.densityDpi = 123; assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); + + // Verify updateConfiguration checks the current DisplayLayout + verify(mDisplayController, times(5)) // init * 1 + updateConfiguration * 4 + .getDisplayLayout(anyInt()); } @Test @@ -168,6 +176,14 @@ public class SplitLayoutTests extends ShellTestCase { verify(mWct).setSmallestScreenWidthDp(eq(task2.token), anyInt()); } + @Test + public void testRoateTo_checksDisplayLayout() { + mSplitLayout.rotateTo(90); + + verify(mDisplayController, times(2)) // init * 1 + rotateTo * 1 + .getDisplayLayout(anyInt()); + } + private void waitDividerFlingFinished() { verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), anyInt(), mRunnableCaptor.capture()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java index ada3455fae18..1dfdbf6514ba 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java @@ -29,6 +29,7 @@ import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; +import android.view.ViewConfiguration; import androidx.test.filters.SmallTest; @@ -90,6 +91,8 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { private PipDisplayLayoutState mPipDisplayLayoutState; + private PipTouchState mPipTouchState; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -104,8 +107,12 @@ public class PipResizeGestureHandlerTest extends ShellTestCase { final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState, mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm, mMockPipTransitionController, mFloatingContentCoordinator); + + mPipTouchState = new PipTouchState(ViewConfiguration.get(mContext), + () -> {}, () -> {}, mMainExecutor); mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm, - mPipBoundsState, motionHelper, mPipTaskOrganizer, mPipDismissTargetHandler, + mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer, + mPipDismissTargetHandler, (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController, mMainExecutor) { @Override diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java index 425bbf056b85..1379aedc2284 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java @@ -106,21 +106,21 @@ public class PipSizeSpecHandlerTest extends ShellTestCase { sExpectedDefaultSizes = new HashMap<>(); sExpectedMinSizes = new HashMap<>(); - sExpectedMaxSizes.put(16f / 9, new Size(1000, 562)); - sExpectedDefaultSizes.put(16f / 9, new Size(600, 337)); - sExpectedMinSizes.put(16f / 9, new Size(499, 281)); + sExpectedMaxSizes.put(16f / 9, new Size(1000, 563)); + sExpectedDefaultSizes.put(16f / 9, new Size(600, 338)); + sExpectedMinSizes.put(16f / 9, new Size(501, 282)); sExpectedMaxSizes.put(4f / 3, new Size(892, 669)); sExpectedDefaultSizes.put(4f / 3, new Size(535, 401)); - sExpectedMinSizes.put(4f / 3, new Size(445, 334)); + sExpectedMinSizes.put(4f / 3, new Size(447, 335)); sExpectedMaxSizes.put(3f / 4, new Size(669, 892)); sExpectedDefaultSizes.put(3f / 4, new Size(401, 535)); - sExpectedMinSizes.put(3f / 4, new Size(334, 445)); + sExpectedMinSizes.put(3f / 4, new Size(335, 447)); sExpectedMaxSizes.put(9f / 16, new Size(562, 999)); sExpectedDefaultSizes.put(9f / 16, new Size(337, 599)); - sExpectedMinSizes.put(9f / 16, new Size(281, 499)); + sExpectedMinSizes.put(9f / 16, new Size(281, 500)); } private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes, 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 66b6c62f1dd6..2dcdc74e8ae7 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 @@ -159,7 +159,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); verify(mMainStage).reparentTopTask(eq(wct)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); @@ -177,7 +177,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); } @@ -189,7 +189,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 81fc8438eec0..1b389565c066 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -128,8 +128,8 @@ public class TaskViewTest extends ShellTestCase { doReturn(true).when(mTransitions).isRegistered(); } mTaskViewTransitions = spy(new TaskViewTransitions(mTransitions)); - mTaskViewTaskController = new TaskViewTaskController(mContext, mOrganizer, - mTaskViewTransitions, mSyncQueue); + mTaskViewTaskController = spy(new TaskViewTaskController(mContext, mOrganizer, + mTaskViewTransitions, mSyncQueue)); mTaskView = new TaskView(mContext, mTaskViewTaskController); mTaskView.setListener(mExecutor, mViewListener); } @@ -544,4 +544,23 @@ public class TaskViewTest extends ShellTestCase { mTaskView.removeTask(); verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController)); } + + @Test + public void testOnTaskAppearedWithTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTaskController.setTaskNotFound(); + mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); + + verify(mTaskViewTaskController).cleanUpPendingTask(); + verify(mTaskViewTransitions).closeTaskView(any(), eq(mTaskViewTaskController)); + } + + @Test + public void testOnTaskAppeared_withoutTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + mTaskViewTaskController.onTaskAppeared(mTaskInfo, mLeash); + verify(mTaskViewTaskController, never()).cleanUpPendingTask(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java index 71ad0d79eaca..03ed18c86608 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java @@ -17,6 +17,7 @@ package com.android.wm.shell.taskview; import static android.view.WindowManager.TRANSIT_CHANGE; +import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.google.common.truth.Truth.assertThat; @@ -25,16 +26,19 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.graphics.Rect; +import android.os.IBinder; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.transition.Transitions; @@ -45,6 +49,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.List; @SmallTest @@ -295,4 +300,34 @@ public class TaskViewTransitionsTest extends ShellTestCase { mTaskViewTransitions.setTaskBounds(mTaskViewTaskController, new Rect(0, 0, 100, 100)); } + + @Test + public void test_startAnimation_setsTaskNotFound() { + assumeTrue(Transitions.ENABLE_SHELL_TRANSITIONS); + + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + when(change.getTaskInfo()).thenReturn(mTaskInfo); + when(change.getMode()).thenReturn(TRANSIT_OPEN); + + List<TransitionInfo.Change> changes = new ArrayList<>(); + changes.add(change); + + TransitionInfo info = mock(TransitionInfo.class); + when(info.getChanges()).thenReturn(changes); + + mTaskViewTransitions.startTaskView(new WindowContainerTransaction(), + mTaskViewTaskController, + mock(IBinder.class)); + + TaskViewTransitions.PendingTransition pending = + mTaskViewTransitions.findPendingOpeningTransition(mTaskViewTaskController); + + mTaskViewTransitions.startAnimation(pending.mClaimed, + info, + new SurfaceControl.Transaction(), + new SurfaceControl.Transaction(), + mock(Transitions.TransitionFinishCallback.class)); + + verify(mTaskViewTaskController).setTaskNotFound(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 963632b1f8f6..ff380e92322d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -51,7 +51,6 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.after; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; @@ -93,7 +92,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.server.testutils.StubTransaction; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.TransitionInfoBuilder; @@ -105,6 +103,7 @@ import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellSharedConstants; +import com.android.wm.shell.util.StubTransaction; import org.junit.Before; import org.junit.Test; @@ -703,8 +702,8 @@ public class ShellTransitionTests extends ShellTestCase { createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); final DisplayController displays = createTestDisplayController(); - final @Surface.Rotation int upsideDown = displays - .getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation(); + final DisplayLayout displayLayout = displays.getDisplayLayout(DEFAULT_DISPLAY); + final @Surface.Rotation int upsideDown = displayLayout.getUpsideDownRotation(); TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE) .setFlags(FLAG_IS_DISPLAY).setRotate().build(); @@ -744,7 +743,8 @@ public class ShellTransitionTests extends ShellTestCase { assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( displayChange, noTask, displays)); - // Not seamless if one of rotations is upside-down + // Not seamless if the nav bar cares rotation and one of rotations is upside-down. + doReturn(false).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving(); displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY) .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build(); final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/StubTransaction.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/StubTransaction.java new file mode 100644 index 000000000000..855f5416fd0f --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/StubTransaction.java @@ -0,0 +1,313 @@ +/* + * 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.wm.shell.util; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.Region; +import android.hardware.HardwareBuffer; +import android.os.IBinder; +import android.os.Parcel; +import android.view.InputWindowHandle; +import android.view.Surface; +import android.view.SurfaceControl; + +import java.util.HashSet; +import java.util.concurrent.Executor; + +/** + * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit + * testing to avoid calls to native code. + * + * Note: This is a copy of com.android.server.testutils.StubTransaction + */ +public class StubTransaction extends SurfaceControl.Transaction { + + private HashSet<Runnable> mWindowInfosReportedListeners = new HashSet<>(); + + @Override + public void apply() { + for (Runnable listener : mWindowInfosReportedListeners) { + listener.run(); + } + } + + @Override + public void close() { + } + + @Override + public void apply(boolean sync) { + apply(); + } + + @Override + public SurfaceControl.Transaction setVisibility(SurfaceControl sc, boolean visible) { + return this; + } + + @Override + public SurfaceControl.Transaction show(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction hide(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setPosition(SurfaceControl sc, float x, float y) { + return this; + } + + @Override + public SurfaceControl.Transaction setBufferSize(SurfaceControl sc, + int w, int h) { + return this; + } + + @Override + public SurfaceControl.Transaction setLayer(SurfaceControl sc, int z) { + return this; + } + + @Override + public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, + int z) { + return this; + } + + @Override + public SurfaceControl.Transaction setTransparentRegionHint(SurfaceControl sc, + Region transparentRegion) { + return this; + } + + @Override + public SurfaceControl.Transaction setAlpha(SurfaceControl sc, float alpha) { + return this; + } + + @Override + public SurfaceControl.Transaction setInputWindowInfo(SurfaceControl sc, + InputWindowHandle handle) { + return this; + } + + @Override + public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop, + Rect destFrame, @Surface.Rotation int orientation) { + return this; + } + + @Override + public SurfaceControl.Transaction setMatrix(SurfaceControl sc, + float dsdx, float dtdx, float dtdy, float dsdy) { + return this; + } + + @Override + public SurfaceControl.Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorTransform(SurfaceControl sc, float[] matrix, + float[] translation) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, Rect crop) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, int width, int height) { + return this; + } + + @Override + @NonNull + public SurfaceControl.Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) { + return this; + } + + @Override + public SurfaceControl.Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) { + return this; + } + + @Override + public SurfaceControl.Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) { + return this; + } + + @Override + public SurfaceControl.Transaction setLayerStack(SurfaceControl sc, int layerStack) { + return this; + } + + @Override + public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) { + return this; + } + + @Override + public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) { + return this; + } + + @Override + public SurfaceControl.Transaction setSecure(SurfaceControl sc, boolean isSecure) { + return this; + } + + @Override + public SurfaceControl.Transaction setOpaque(SurfaceControl sc, boolean isOpaque) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplaySurface(IBinder displayToken, Surface surface) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayFlags(IBinder displayToken, int flags) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken, + int orientation, Rect layerStackRect, Rect displayRect) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplaySize(IBinder displayToken, int width, int height) { + return this; + } + + @Override + public SurfaceControl.Transaction setAnimationTransaction() { + return this; + } + + @Override + public SurfaceControl.Transaction setMetadata(SurfaceControl sc, int key, int data) { + return this; + } + + @Override + public SurfaceControl.Transaction setMetadata(SurfaceControl sc, int key, Parcel data) { + return this; + } + + @Override + public SurfaceControl.Transaction merge(SurfaceControl.Transaction other) { + return this; + } + + @Override + public SurfaceControl.Transaction remove(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction addTransactionCommittedListener(Executor executor, + SurfaceControl.TransactionCommittedListener listener) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorSpaceAgnostic(SurfaceControl sc, boolean agnostic) { + return this; + } + + @Override + public SurfaceControl.Transaction setFrameRateSelectionPriority(SurfaceControl sc, + int priority) { + return this; + } + + @Override + public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate, + int compatibility, int changeFrameRateStrategy) { + return this; + } + + @Override + public SurfaceControl.Transaction unsetColor(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) { + return this; + } + + @Override + public SurfaceControl.Transaction setFixedTransformHint(SurfaceControl sc, + @Surface.Rotation int transformHint) { + return this; + } + + @Override + public SurfaceControl.Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) { + return this; + } + + @Override + @NonNull + public SurfaceControl.Transaction setBuffer(@NonNull SurfaceControl sc, + @Nullable HardwareBuffer buffer) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorSpace(SurfaceControl sc, ColorSpace colorSpace) { + return this; + } + + @Override + public SurfaceControl.Transaction setTrustedOverlay(SurfaceControl sc, + boolean isTrustedOverlay) { + return this; + } + + @Override + public SurfaceControl.Transaction addWindowInfosReportedListener(@NonNull Runnable listener) { + mWindowInfosReportedListeners.add(listener); + return this; + } +} diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index 3527eeead1d5..2a6dc7b95c07 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -108,7 +108,7 @@ status_t CursorWindow::maybeInflate() { { // Migrate existing contents into new ashmem region - uint32_t slotsSize = mSize - mSlotsOffset; + uint32_t slotsSize = sizeOfSlots(); uint32_t newSlotsOffset = mInflatedSize - slotsSize; memcpy(static_cast<uint8_t*>(newData), static_cast<uint8_t*>(mData), mAllocOffset); @@ -216,11 +216,9 @@ status_t CursorWindow::writeToParcel(Parcel* parcel) { if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail; } else { // Since we know we're going to be read-only on the remote side, - // we can compact ourselves on the wire, with just enough padding - // to ensure our slots stay aligned - size_t slotsSize = mSize - mSlotsOffset; - size_t compactedSize = mAllocOffset + slotsSize; - compactedSize = (compactedSize + 3) & ~3; + // we can compact ourselves on the wire. + size_t slotsSize = sizeOfSlots(); + size_t compactedSize = sizeInUse(); if (parcel->writeUint32(compactedSize)) goto fail; if (parcel->writeBool(false)) goto fail; void* dest = parcel->writeInplace(compactedSize); diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h index 6e55a9a0eb8b..9ec026a19c4c 100644 --- a/libs/androidfw/include/androidfw/CursorWindow.h +++ b/libs/androidfw/include/androidfw/CursorWindow.h @@ -90,6 +90,9 @@ public: inline uint32_t getNumRows() { return mNumRows; } inline uint32_t getNumColumns() { return mNumColumns; } + inline size_t sizeOfSlots() const { return mSize - mSlotsOffset; } + inline size_t sizeInUse() const { return mAllocOffset + sizeOfSlots(); } + status_t clear(); status_t setNumColumns(uint32_t numColumns); diff --git a/libs/androidfw/tests/CursorWindow_test.cpp b/libs/androidfw/tests/CursorWindow_test.cpp index d1cfd03276c2..3cdb31e982b1 100644 --- a/libs/androidfw/tests/CursorWindow_test.cpp +++ b/libs/androidfw/tests/CursorWindow_test.cpp @@ -21,9 +21,16 @@ #include "TestHelpers.h" +// Verify that the memory in use is a multiple of 4 bytes +#define ASSERT_ALIGNED(w) \ + ASSERT_EQ(((w)->sizeInUse() & 3), 0); \ + ASSERT_EQ(((w)->freeSpace() & 3), 0); \ + ASSERT_EQ(((w)->sizeOfSlots() & 3), 0) + #define CREATE_WINDOW_1K \ CursorWindow* w; \ - CursorWindow::create(String8("test"), 1 << 10, &w); + CursorWindow::create(String8("test"), 1 << 10, &w); \ + ASSERT_ALIGNED(w); #define CREATE_WINDOW_1K_3X3 \ CursorWindow* w; \ @@ -31,11 +38,13 @@ ASSERT_EQ(w->setNumColumns(3), OK); \ ASSERT_EQ(w->allocRow(), OK); \ ASSERT_EQ(w->allocRow(), OK); \ - ASSERT_EQ(w->allocRow(), OK); + ASSERT_EQ(w->allocRow(), OK); \ + ASSERT_ALIGNED(w); #define CREATE_WINDOW_2M \ CursorWindow* w; \ - CursorWindow::create(String8("test"), 1 << 21, &w); + CursorWindow::create(String8("test"), 1 << 21, &w); \ + ASSERT_ALIGNED(w); static constexpr const size_t kHalfInlineSize = 8192; static constexpr const size_t kGiantSize = 1048576; @@ -49,6 +58,7 @@ TEST(CursorWindowTest, Empty) { ASSERT_EQ(w->getNumColumns(), 0); ASSERT_EQ(w->size(), 1 << 10); ASSERT_EQ(w->freeSpace(), 1 << 10); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, SetNumColumns) { @@ -60,6 +70,7 @@ TEST(CursorWindowTest, SetNumColumns) { ASSERT_NE(w->setNumColumns(5), OK); ASSERT_NE(w->setNumColumns(3), OK); ASSERT_EQ(w->getNumColumns(), 4); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, SetNumColumnsAfterRow) { @@ -70,6 +81,7 @@ TEST(CursorWindowTest, SetNumColumnsAfterRow) { ASSERT_EQ(w->allocRow(), OK); ASSERT_NE(w->setNumColumns(4), OK); ASSERT_EQ(w->getNumColumns(), 0); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, AllocRow) { @@ -83,14 +95,17 @@ TEST(CursorWindowTest, AllocRow) { ASSERT_EQ(w->allocRow(), OK); ASSERT_LT(w->freeSpace(), before); ASSERT_EQ(w->getNumRows(), 1); + ASSERT_ALIGNED(w); // Verify we can unwind ASSERT_EQ(w->freeLastRow(), OK); ASSERT_EQ(w->freeSpace(), before); ASSERT_EQ(w->getNumRows(), 0); + ASSERT_ALIGNED(w); // Can't unwind when no rows left ASSERT_NE(w->freeLastRow(), OK); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, AllocRowBounds) { @@ -100,6 +115,7 @@ TEST(CursorWindowTest, AllocRowBounds) { ASSERT_EQ(w->setNumColumns(60), OK); ASSERT_EQ(w->allocRow(), OK); ASSERT_NE(w->allocRow(), OK); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreNull) { @@ -116,6 +132,7 @@ TEST(CursorWindowTest, StoreNull) { auto field = w->getFieldSlot(0, 0); ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreLong) { @@ -134,6 +151,7 @@ TEST(CursorWindowTest, StoreLong) { ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreString) { @@ -155,6 +173,7 @@ TEST(CursorWindowTest, StoreString) { auto actual = w->getFieldSlotValueString(field, &size); ASSERT_EQ(std::string(actual), "cafe"); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, StoreBounds) { @@ -175,6 +194,7 @@ TEST(CursorWindowTest, StoreBounds) { ASSERT_EQ(w->getFieldSlot(-1, 0), nullptr); ASSERT_EQ(w->getFieldSlot(0, -1), nullptr); ASSERT_EQ(w->getFieldSlot(-1, -1), nullptr); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, Inflate) { @@ -234,6 +254,7 @@ TEST(CursorWindowTest, Inflate) { ASSERT_NE(actual, buf); ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, ParcelEmpty) { @@ -249,10 +270,12 @@ TEST(CursorWindowTest, ParcelEmpty) { ASSERT_EQ(w->getNumColumns(), 0); ASSERT_EQ(w->size(), 0); ASSERT_EQ(w->freeSpace(), 0); + ASSERT_ALIGNED(w); // We can't mutate the window after parceling ASSERT_NE(w->setNumColumns(4), OK); ASSERT_NE(w->allocRow(), OK); + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, ParcelSmall) { @@ -311,6 +334,7 @@ TEST(CursorWindowTest, ParcelSmall) { ASSERT_EQ(actualSize, 0); ASSERT_NE(actual, nullptr); } + ASSERT_ALIGNED(w); } TEST(CursorWindowTest, ParcelLarge) { @@ -364,6 +388,7 @@ TEST(CursorWindowTest, ParcelLarge) { ASSERT_EQ(actualSize, 0); ASSERT_NE(actual, nullptr); } + ASSERT_ALIGNED(w); } } // android diff --git a/libs/hwui/jni/Gainmap.cpp b/libs/hwui/jni/Gainmap.cpp index 0f8a85dd9e62..cec0ee7ee247 100644 --- a/libs/hwui/jni/Gainmap.cpp +++ b/libs/hwui/jni/Gainmap.cpp @@ -86,6 +86,16 @@ jlong Gainmap_createEmpty(JNIEnv*, jobject) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap)); } +jlong Gainmap_createCopy(JNIEnv*, jobject, jlong sourcePtr) { + Gainmap* gainmap = new Gainmap(); + gainmap->incStrong(0); + if (sourcePtr) { + Gainmap* src = fromJava(sourcePtr); + gainmap->info = src->info; + } + return static_cast<jlong>(reinterpret_cast<uintptr_t>(gainmap)); +} + static void Gainmap_setBitmap(JNIEnv* env, jobject, jlong gainmapPtr, jobject jBitmap) { android::Bitmap* bitmap = GraphicsJNI::getNativeBitmap(env, jBitmap); fromJava(gainmapPtr)->bitmap = sk_ref_sp(bitmap); @@ -237,6 +247,7 @@ static void Gainmap_readFromParcel(JNIEnv* env, jobject, jlong nativeObject, job static const JNINativeMethod gGainmapMethods[] = { {"nGetFinalizer", "()J", (void*)Gainmap_getNativeFinalizer}, {"nCreateEmpty", "()J", (void*)Gainmap_createEmpty}, + {"nCreateCopy", "(J)J", (void*)Gainmap_createCopy}, {"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*)Gainmap_setBitmap}, {"nSetRatioMin", "(JFFF)V", (void*)Gainmap_setRatioMin}, {"nGetRatioMin", "(J[F)V", (void*)Gainmap_getRatioMin}, diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 32680dae75b9..38bb447f8a16 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -359,6 +359,51 @@ import java.util.concurrent.locks.ReentrantLock; codec to use a previously created {@linkplain #createPersistentInputSurface persistent input surface} by calling {@link #setInputSurface}. + <h4 id=EncoderProfiles><a name="EncoderProfiles"></a>Encoder Profiles</h4> + <p> + When using an encoder, it is recommended to set the desired codec {@link MediaFormat#KEY_PROFILE + profile} during {@link #configure configure()}. (This is only meaningful for + {@link MediaFormat#KEY_MIME media formats} for which profiles are defined.) + <p> + If a profile is not specified during {@code configure}, the encoder will choose a profile for the + session based on the available information. We will call this value the <i>default profile</i>. + The selection of the default profile is device specific and may not be deterministic + (could be ad hoc or even experimental). The encoder may choose a default profile that is not + suitable for the intended encoding session, which may result in the encoder ultimately rejecting + the session. + <p> + The encoder may reject the encoding session if the configured (or default if unspecified) profile + does not support the codec input (mainly the {@link MediaFormat#KEY_COLOR_FORMAT color format} for + video/image codecs, or the {@link MediaFormat#KEY_PCM_ENCODING sample encoding} and the {@link + MediaFormat#KEY_CHANNEL_COUNT number of channels} for audio codecs, but also possibly + {@link MediaFormat#KEY_WIDTH width}, {@link MediaFormat#KEY_HEIGHT height}, + {@link MediaFormat#KEY_FRAME_RATE frame rate}, {@link MediaFormat#KEY_BIT_RATE bitrate} or + {@link MediaFormat#KEY_SAMPLE_RATE sample rate}.) + Alternatively, the encoder may choose to (but is not required to) convert the input to support the + selected (or default) profile - or adjust the chosen profile based on the presumed or detected + input format - to ensure a successful encoding session. <b>Note</b>: Converting the input to match + an incompatible profile will in most cases result in decreased codec performance. + <p> + To ensure backward compatibility, the following guarantees are provided by Android: + <ul> + <li>The default video encoder profile always supports 8-bit YUV 4:2:0 color format ({@link + CodecCapabilities#COLOR_FormatYUV420Flexible COLOR_FormatYUV420Flexible} and equivalent + {@link CodecCapabilities#colorFormats supported formats}) for both Surface and ByteBuffer modes. + <li>The default video encoder profile always supports the default 8-bit RGBA color format in + Surface mode even if no such formats are enumerated in the {@link CodecCapabilities#colorFormats + supported formats}. + </ul> + <p class=note> + <b>Note</b>: the accepted profile can be queried through the {@link #getOutputFormat output + format} of the encoder after {@code configure} to allow applications to set up their + codec input to a format supported by the encoder profile. + <p> + <b>Implication:</b> + <ul> + <li>Applications that want to encode 4:2:2, 4:4:4, 10+ bit or HDR video input <b>MUST</b> configure + a suitable profile for encoders. + </ul> + <h4 id=CSD><a name="CSD"></a>Codec-specific Data</h4> <p> Some formats, notably AAC audio and MPEG4, H.264 and H.265 video formats require the actual data diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index b1b7d4000635..46db77708521 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1136,11 +1136,14 @@ public final class MediaFormat { * may fail if other parameters are not compatible with the desired * profile or if the desired profile is not supported, but it may also * fail silently (where the encoder ends up using a different, compatible profile.) + * <p> + * It is recommended that the profile is set for all encoders. For more information, see + * the <i>Encoder Profiles</i> section of the {@link MediaCodec} API reference. * <p class="note"> * <strong>Note:</strong> Codecs are free to use all the available * coding tools at the specified profile, but may ultimately choose to not do so. * <p class="note"> - * <strong>Note:</strong> When configuring video encoders, profile must be + * <strong>Note:</strong> When configuring video encoders, profile (if set) must be * set together with {@link #KEY_LEVEL level}. * * @see MediaCodecInfo.CodecCapabilities#profileLevels diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index ec7361971e8b..39ffcd7f975d 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -65,7 +65,7 @@ <string name="permission_contacts_summary" msgid="675861979475628708">"Dapat mengakses kontak Anda"</string> <string name="permission_calendar_summary" msgid="6460000922511766226">"Dapat mengakses kalender Anda"</string> <string name="permission_microphone_summary" msgid="3692091540613093394">"Dapat merekam audio"</string> - <string name="permission_nearby_devices_summary" msgid="931940524460876655">"Dapat menemukan, menghubungkan, dan menentukan posisi relatif dari perangkat di sekitar"</string> + <string name="permission_nearby_devices_summary" msgid="931940524460876655">"Dapat menemukan, terhubung ke, dan menentukan posisi relatif perangkat di sekitar"</string> <string name="permission_notification_summary" msgid="884075314530071011">"Dapat membaca semua notifikasi, termasuk informasi seperti kontak, pesan, dan foto"</string> <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streaming aplikasi ponsel"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 8128d8a64c7b..b131a2fa53af 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -31,7 +31,7 @@ <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> - <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 휴대전화에서 이 정보에 액세스하도록 허용"</string> + <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>이 휴대전화에서 이 정보에 액세스하도록 허용"</string> <string name="summary_computer" msgid="3798467601598297062"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 서비스"</string> <string name="helper_summary_computer" msgid="8774832742608187072">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 휴대전화의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index 7514ee3157fa..220f00e10e06 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -69,7 +69,7 @@ <string name="permission_notification_summary" msgid="884075314530071011">"Môže čítať všetky upozornenia vrátane informácií, ako sú kontakty, správy a fotky"</string> <string name="permission_app_streaming_summary" msgid="606923325679670624">"Streamovať aplikácie telefónu"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> - <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Steaming aplikácii a ďalších systémov funkcií zo zariadenia"</string> + <string name="permission_nearby_device_streaming_summary" msgid="8280824871197081246">"Streaming aplikácii a ďalších systémových funkcií zo zariadenia"</string> <string name="device_type" product="default" msgid="8268703872070046263">"telefón"</string> <string name="device_type" product="tablet" msgid="5038791954983067774">"tablet"</string> </resources> diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml index de78b38cae84..155be6fc90db 100644 --- a/packages/CredentialManager/res/values-fr-rCA/strings.xml +++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml @@ -75,7 +75,7 @@ <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Choisissez une clé d\'accès enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Choisissez un mot de passe enregistré pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Choisissez une connexion enregistrée pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> - <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Choisir un identifiant de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> + <string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"Choisissez un identifiant de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"Choisir une option pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Utiliser ces renseignements dans <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"Se connecter d\'une autre manière"</string> diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml index 2cbfce8a42f7..0e170254ab4b 100644 --- a/packages/CredentialManager/res/values-vi/strings.xml +++ b/packages/CredentialManager/res/values-vi/strings.xml @@ -61,7 +61,7 @@ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mật khẩu"</string> <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> khoá truy cập"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> thông tin xác thực"</string> - <string name="passkey_before_subtitle" msgid="2448119456208647444">"Mã xác thực"</string> + <string name="passkey_before_subtitle" msgid="2448119456208647444">"Khoá truy cập"</string> <string name="another_device" msgid="5147276802037801217">"Thiết bị khác"</string> <string name="other_password_manager" msgid="565790221427004141">"Trình quản lý mật khẩu khác"</string> <string name="close_sheet" msgid="1393792015338908262">"Đóng trang tính"</string> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 2dafbcb95205..a78509d897aa 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -66,8 +66,9 @@ class CredentialManagerRepo( ) val originName: String? = when (requestInfo?.type) { - RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin - RequestInfo.TYPE_GET -> requestInfo.getCredentialRequest?.origin + RequestInfo.TYPE_CREATE -> processHttpsOrigin( + requestInfo.createCredentialRequest?.origin) + RequestInfo.TYPE_GET -> processHttpsOrigin(requestInfo.getCredentialRequest?.origin) else -> null } @@ -247,6 +248,9 @@ class CredentialManagerRepo( } companion object { + private const val HTTPS = "https://" + private const val FORWARD_SLASH = "/" + fun sendCancellationCode( cancelCode: Int, requestToken: IBinder?, @@ -266,5 +270,17 @@ class CredentialManagerRepo( CancelUiRequest::class.java ) } + + /** Removes "https://", and the trailing slash if present for an https request. */ + private fun processHttpsOrigin(origin: String?): String? { + var processed = origin + if (processed?.startsWith(HTTPS) == true) { // Removes "https://" + processed = processed.substring(HTTPS.length) + if (processed?.endsWith(FORWARD_SLASH) == true) { // Removes the trailing slash + processed = processed.substring(0, processed.length - 1) + } + } + return processed + } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index cf962d1d94aa..bce86c477e77 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -118,8 +118,10 @@ class CredentialSelectorViewModel( if (entry != null && entry.pendingIntent != null) { Log.d(Constants.LOG_TAG, "Launching provider activity") uiState = uiState.copy(providerActivityState = ProviderActivityState.PENDING) + val entryIntent = entry.fillInIntent + entryIntent?.putExtra(Constants.IS_AUTO_SELECTED_KEY, uiState.isAutoSelectFlow) val intentSenderRequest = IntentSenderRequest.Builder(entry.pendingIntent) - .setFillInIntent(entry.fillInIntent).build() + .setFillInIntent(entryIntent).build() try { launcher.launch(intentSenderRequest) } catch (e: Exception) { diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt index c6dc5945d886..51ca5971cec4 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt @@ -21,5 +21,6 @@ class Constants { const val LOG_TAG = "CredentialSelector" const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS = "androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED" + const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED" } -}
\ No newline at end of file +} diff --git a/packages/PrintSpooler/res/values-mk/strings.xml b/packages/PrintSpooler/res/values-mk/strings.xml index d96330c43daf..f4802556a439 100644 --- a/packages/PrintSpooler/res/values-mk/strings.xml +++ b/packages/PrintSpooler/res/values-mk/strings.xml @@ -46,7 +46,7 @@ <string name="print_button" msgid="645164566271246268">"Печати"</string> <string name="savetopdf_button" msgid="2976186791686924743">"Зачувај во PDF"</string> <string name="print_options_expanded" msgid="6944679157471691859">"Опциите на печатачот се прикажани"</string> - <string name="print_options_collapsed" msgid="7455930445670414332">"Опциите на печатачот се сокриени"</string> + <string name="print_options_collapsed" msgid="7455930445670414332">"Опциите на печатачот се скриени"</string> <string name="search" msgid="5421724265322228497">"Пребарај"</string> <string name="all_printers_label" msgid="3178848870161526399">"Сите печатачи"</string> <string name="add_print_service_label" msgid="5356702546188981940">"Додајте услуга"</string> diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 750c156f95bd..c244ca0505ed 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -69,9 +69,6 @@ android_library { "src/**/*.java", "src/**/*.kt", ], - - min_sdk_version: "30", - } // NOTE: Keep this module in sync with ./common.mk diff --git a/packages/SettingsLib/ProfileSelector/res/values-te/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-te/strings.xml index 75ee30fbd367..9a44dcf7e269 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-te/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-te/strings.xml @@ -18,5 +18,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"వ్యక్తిగతం"</string> - <string name="settingslib_category_work" msgid="4867750733682444676">"ఆఫీస్"</string> + <string name="settingslib_category_work" msgid="4867750733682444676">"వర్క్"</string> </resources> diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt index 89bfa0eb646b..030b70a75a8b 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt @@ -31,6 +31,8 @@ import com.android.settingslib.spaprivileged.template.common.UserProfilePager /** * The full screen template for an App List page. * + * @param noMoreOptions default false. If true, then do not display more options action button, + * including the "Show System" / "Hide System" action. * @param header the description header appears before all the applications. */ @Composable @@ -38,6 +40,7 @@ fun <T : AppRecord> AppListPage( title: String, listModel: AppListModel<T>, showInstantApps: Boolean = false, + noMoreOptions: Boolean = false, matchAnyUserForAdmin: Boolean = false, primaryUserOnly: Boolean = false, noItemMessage: String? = null, @@ -49,9 +52,11 @@ fun <T : AppRecord> AppListPage( SearchScaffold( title = title, actions = { - MoreOptionsAction { - ShowSystemAction(showSystem.value) { showSystem.value = it } - moreOptions() + if (!noMoreOptions) { + MoreOptionsAction { + ShowSystemAction(showSystem.value) { showSystem.value = it } + moreOptions() + } } }, ) { bottomPadding, searchQuery -> diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt index 06003c0cb8f9..f6f48891030a 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListPageTest.kt @@ -63,9 +63,7 @@ class AppListPageTest { fun canShowSystem() { val inputState by setContent() - composeTestRule.onNodeWithContentDescription( - context.getString(R.string.abc_action_menu_overflow_description) - ).performClick() + onMoreOptions().performClick() composeTestRule.onNodeWithText(context.getString(R.string.menu_show_system)).performClick() val state = inputState!!.state @@ -75,20 +73,32 @@ class AppListPageTest { @Test fun afterShowSystem_displayHideSystem() { setContent() - composeTestRule.onNodeWithContentDescription( - context.getString(R.string.abc_action_menu_overflow_description) - ).performClick() + onMoreOptions().performClick() composeTestRule.onNodeWithText(context.getString(R.string.menu_show_system)).performClick() - composeTestRule.onNodeWithContentDescription( - context.getString(R.string.abc_action_menu_overflow_description) - ).performClick() + onMoreOptions().performClick() composeTestRule.onNodeWithText(context.getString(R.string.menu_hide_system)) .assertIsDisplayed() } + @Test + fun noMoreOptions_notDisplayMoreOptions() { + setContent(noMoreOptions = true) + + onMoreOptions().assertDoesNotExist() + } + + @Test + fun noMoreOptions_showSystemIsFalse() { + val inputState by setContent(noMoreOptions = true) + + val state = inputState!!.state + assertThat(state.showSystem.value).isFalse() + } + private fun setContent( + noMoreOptions: Boolean = false, header: @Composable () -> Unit = {}, ): State<AppListInput<TestAppRecord>?> { val appListState = mutableStateOf<AppListInput<TestAppRecord>?>(null) @@ -96,6 +106,7 @@ class AppListPageTest { AppListPage( title = TITLE, listModel = TestAppListModel(), + noMoreOptions = noMoreOptions, header = header, appList = { appListState.value = this }, ) @@ -103,6 +114,11 @@ class AppListPageTest { return appListState } + private fun onMoreOptions() = + composeTestRule.onNodeWithContentDescription( + context.getString(R.string.abc_action_menu_overflow_description) + ) + private companion object { const val TITLE = "Title" } diff --git a/packages/SettingsLib/res/drawable/ic_docked_tablet.xml b/packages/SettingsLib/res/drawable/ic_docked_tablet.xml new file mode 100644 index 000000000000..96a4900f361f --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_docked_tablet.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="#000000" + android:pathData="M480,280Q497,280 508.5,268.5Q520,257 520,240Q520,223 508.5,211.5Q497,200 480,200Q463,200 451.5,211.5Q440,223 440,240Q440,257 451.5,268.5Q463,280 480,280ZM120,720Q87,720 63.5,696.5Q40,673 40,640L60,160Q60,127 83.5,103.5Q107,80 140,80L820,80Q853,80 876.5,103.5Q900,127 900,160L920,640Q920,673 896.5,696.5Q873,720 840,720L120,720ZM120,640L840,640Q840,640 840,640Q840,640 840,640L820,160Q820,160 820,160Q820,160 820,160L140,160Q140,160 140,160Q140,160 140,160L120,640Q120,640 120,640Q120,640 120,640ZM320,880Q259,880 209.5,850Q160,820 160,765L160,720L240,720L240,760Q253,780 274.5,790Q296,800 320,800L640,800Q664,800 685.5,790.5Q707,781 720,761L720,720L800,720L800,765Q800,820 750.5,850Q701,880 640,880L320,880ZM480,400Q480,400 480,400Q480,400 480,400L480,400Q480,400 480,400Q480,400 480,400L480,400Q480,400 480,400Q480,400 480,400L480,400Q480,400 480,400Q480,400 480,400L480,400Z"/> +</vector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 0e6f207b2060..98ad6e33ce3d 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Ontkoppel"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ontkoppel tans…"</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Verbind tans…"</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Verbind tans …"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Gekoppel<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Verbind tans…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Gekoppel (geen foon nie)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Sopas"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Hierdie foon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Hierdie tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokluidspreker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Gekoppelde toestel"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan nie op hierdie toestel speel nie"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Gradeer rekening op om oor te skakel"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index a45359469280..b8b1b69b8054 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ልክ አሁን"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ይህ ስልክ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ይህ ጡባዊ"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"የመትከያ ድምጽ ማውጫ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"የተገናኘ መሣሪያ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"በዚህ መሣሪያ ላይ ማጫወት አልተቻለም"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ለመቀየር መለያ ያልቁ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 1a973488f87f..18dc07f92b52 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"هذا الهاتف"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"هذا الجهاز اللوحي"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"مكبّر صوت بقاعدة إرساء"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"جهاز متّصل"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"لا يمكن تشغيل الوسائط هنا"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"يجب ترقية الحساب للتبديل"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 7419665fd18e..725a0572e70d 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"এই মাত্ৰ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"এই ফ’নটো"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"এই টেবলেটটো"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ড’ক স্পীকাৰ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"সংযোগ হৈ থকা ডিভাইচ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইচটো প্লে\' কৰিব নোৱাৰি"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"সলনি কৰিবলৈ একাউণ্ট আপগ্ৰে’ড কৰক"</string> @@ -579,7 +582,7 @@ <string name="user_add_user_message_short" msgid="3295959985795716166">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ ঠাই ছেট আপ কৰাৰ প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে অন্য সকলো ব্যৱহাৰকাৰীৰ বাবে এপ্ আপডে\'ট কৰিব পাৰে।"</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"এই ব্যৱহাৰকাৰীগৰাকীক এগৰাকী প্ৰশাসক বনাবনে?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"প্ৰশাসকৰ ওচৰত কিছুমান বিশেষাধিকাৰ আছে, যিবোৰ অন্য ব্যৱহাৰকাৰীৰ নাই। এগৰাকী প্ৰশাসকে সকলো ব্যৱহাৰকাৰীক পৰিচালনা কৰিব, এই ডিভাইচটো আপডে’ট অথবা ৰিছেট কৰিব, ছেটিং সংশোধন কৰিব, ইনষ্টল কৰি থোৱা আটাইবোৰ এপ্ চাব আৰু অন্য লোকৰ বাবে প্ৰশাসকৰ বিশেষাধিকাৰ প্ৰদান কৰিব অথবা প্ৰত্যাহাৰ কৰিব পাৰে।"</string> - <string name="user_grant_admin_button" msgid="5441486731331725756">"প্ৰশাসকৰ বনাওক"</string> + <string name="user_grant_admin_button" msgid="5441486731331725756">"প্ৰশাসক বনাওক"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"ব্যৱহাৰকাৰী এতিয়া ছেট আপ কৰিবনে?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"ডিভাইচটো লৈ নিজৰ ঠাই ছেটআপ কৰিবলৈ নতুন ব্যৱহাৰকাৰী উপলব্ধ থকাটো নিশ্চিত কৰক"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"এতিয়া প্ৰ\'ফাইল ছেট আপ কৰিবনে?"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 8d4216396757..ec1b8cfe406c 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -455,7 +455,7 @@ <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"İstifadəyə əsasən təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string> <string name="power_discharge_by" msgid="4113180890060388350">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string> - <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> olana qədər"</string> + <string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> radəsinə qədər"</string> <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batareya <xliff:g id="TIME">%1$s</xliff:g> radələrinə qədər boşala bilər"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Maksimum <xliff:g id="THRESHOLD">%1$s</xliff:g> qalıb"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Maksimum <xliff:g id="THRESHOLD">%1$s</xliff:g> qalıb (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"İndicə"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Bu planşet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok dinamiki"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Xarici cihaz"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Qoşulmuş cihaz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oxutmaq mümkün deyil"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Keçirmək üçün hesabı təkmilləşdirin"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Burada endirmələri oxutmaq mümkün deyil"</string> - <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Reklamdan sonra yenidən cəhd edin"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Burada oxutmaq üçün cihazı oyadın"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Cihaz oxudulmaq üçün təsdiqlənməyib"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Bu medianı burada oxutmaq mümkün deyil"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oxutmaq olmur"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Keçirmək üçün hesabı güncəllə"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Burada endirmələri oxutmaq olmur"</string> + <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Reklamdan sonra yenidən sına"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Burada oxutmaqçün cihazı oyat"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Cihaz oxutmaq üçün təsdiqlənməyib"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Bu medianı burada oxutmaq olmur"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string> <string name="help_label" msgid="3528360748637781274">"Yardım və rəy"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 924a53f19757..91ca8a66a9e1 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik bazne stanice"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Spoljni uređaj"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne možete da pustite na ovom uređaju"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite nalog radi prebacivanja"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index a7f189135ea7..aa28a3158abb 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -304,7 +304,7 @@ <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Пры выбары сеткі Wi-Fi указваць у журнале RSSI для кожнага SSID"</string> <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Зніжае расход зараду акумулятара і павышае прадукцыйнасць мабільных сетак"</string> <string name="wifi_non_persistent_mac_randomization_summary" msgid="2159794543105053930">"Калі ўключаны гэты рэжым, MAC-адрас гэтай прылады можа змяняцца падчас кожнага падключэння да сеткі з актыўнай рандамізацыяй MAC-адрасоў."</string> - <string name="wifi_metered_label" msgid="8737187690304098638">"Сетка з улікам трафіка"</string> + <string name="wifi_metered_label" msgid="8737187690304098638">"Сетка з падлікам трафіка"</string> <string name="wifi_unmetered_label" msgid="6174142840934095093">"Сетка без уліку трафіка"</string> <string name="select_logd_size_title" msgid="1604578195914595173">"Памеры буфера журнала"</string> <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Выберыце памеры сродку вядзення журнала для буфераў журнала"</string> @@ -545,6 +545,12 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Толькі што"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Гэты тэлефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Гэты планшэт"</string> + <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> + <skip /> + <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> + <skip /> + <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> + <skip /> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не ўдаецца прайграць на гэтай прыладзе"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Для пераключэння перайдзіце на іншую версію ўліковага запісу"</string> @@ -575,8 +581,8 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Карыстальнік"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Абмежаваны профiль"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Дадаць новага карыстальніка?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Вы можаце адкрыць доступ да гэтай прылады іншым людзям шляхам стварэння дадатковых карыстальнікаў. Кожны карыстальнік мае свой уласны раздзел, на якім ён можа наладзіць свае праграмы, шпалеры і іншае. Карыстальнікі таксама могуць наладжваць параметры прылады, напрыклад Wi-Fi, якія ўплываюць на ўсіх.\n\nКалі вы дадаяце новага карыстальніка, ён павінен наладзіць свой раздзел.\n\nЛюбы карыстальнік можа абнаўляць праграмы для ўсіх астатніх карыстальнікаў. Спецыяльныя магчымасці наладжваюцца асабіста кожным карыстальнікам."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"Пасля стварэння профіля яго трэба наладзіць.\n\nЛюбы карыстальнік прылады можа абнаўляць праграмы ўсіх іншых карыстальнікаў."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Вы можаце адкрыць доступ да гэтай прылады іншым карыстальнікам шляхам стварэння дадатковых профіляў. Кожны карыстальнік будзе мець свой уласны профіль, на якім ён зможа наладзіць свае праграмы, шпалеры і іншае. Карыстальнікі таксама змогуць наладжваць параметры прылады, напрыклад Wi-Fi, якія ўплываюць на ўсіх.\n\nКалі вы дадаяце новага карыстальніка, ён павінен наладзіць свой профіль.\n\nЛюбы карыстальнік можа абнаўляць праграмы для ўсіх астатніх карыстальнікаў. Спецыяльныя магчымасці наладжваюцца асабіста кожным карыстальнікам."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"Пасля стварэння профілю яго трэба наладзіць.\n\nЛюбы карыстальнік прылады можа абнаўляць праграмы ўсіх іншых карыстальнікаў."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Зрабіць гэтага карыстальніка адміністратарам?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Адміністратары маюць спецыяльныя правы, якіх няма ў звычайных карыстальнікаў. Адмінстратар можа кіраваць усімі карыстальнікамі, абнаўляць або скідваць ПЗ прылады, змяняць налады, праглядаць усталяваныя праграмы, даваць іншым карыстальнікам або адклікаць правы адміністратара."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Зрабіць адміністратарам"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 4ce56f07cac6..2779689c41ad 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Току-що"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Този телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Този таблет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Високоговорител докинг станция"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Възпроизвеждането не е възможно на това устройство"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надстройте профила, за да превключите"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index b22f2146f866..68283ca69234 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -86,7 +86,7 @@ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"ডিসকানেক্ট হচ্ছে..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"কানেক্ট হচ্ছে..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"কানেক্ট করা আছে<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"যুক্ত করা হচ্ছে..."</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"পেয়ার করা হচ্ছে..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"কানেক্ট করা আছে (ফোনের অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"কানেক্ট করা আছে (মিডিয়ার অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"কানেক্ট করা আছে (ফোনের বা মিডিয়ার অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"এখনই"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"এই ফোন"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"এই ট্যাবলেট"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ডক স্পিকার"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"কানেক্ট থাকা ডিভাইস"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইসে চালানো যাবে না"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"পাল্টাতে অ্যাকাউন্ট আপগ্রেড করুন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index fd0868667be0..8e589f6e78fe 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nije moguće reproducirati na uređaju"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite račun da promijenite"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index a033313dccea..f22b489b839b 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -182,7 +182,7 @@ <string name="tts_default_lang_title" msgid="4698933575028098940">"Idioma"</string> <string name="tts_lang_use_system" msgid="6312945299804012406">"Utilitza l\'idioma del sistema"</string> <string name="tts_lang_not_selected" msgid="7927823081096056147">"No has seleccionat cap idioma"</string> - <string name="tts_default_lang_summary" msgid="9042620014800063470">"Defineix la llengua utilitzada per a la síntesi de veu"</string> + <string name="tts_default_lang_summary" msgid="9042620014800063470">"Defineix la llengua utilitzada per al text enunciat"</string> <string name="tts_play_example_title" msgid="1599468547216481684">"Vull escoltar un exemple"</string> <string name="tts_play_example_summary" msgid="634044730710636383">"Reprodueix una breu demostració de síntesi de veu"</string> <string name="tts_install_data_title" msgid="1829942496472751703">"Instal·la dades de veu"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Ara mateix"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Aquest telèfon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Aquesta tauleta"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base d\'altaveu"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositiu extern"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositiu connectat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No es pot reproduir en aquest dispositiu"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualitza el compte per canviar"</string> @@ -611,8 +614,8 @@ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Aquesta acció iniciarà una nova sessió de convidat i suprimirà totes les aplicacions i dades de la sessió actual"</string> <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sortir del mode de convidat?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Aquesta acció suprimirà les aplicacions i dades de la sessió de convidat actual"</string> - <string name="grant_admin" msgid="4323199171790522574">"Sí, converteix en administrador"</string> - <string name="not_grant_admin" msgid="3557849576157702485">"No, no converteixis en administrador"</string> + <string name="grant_admin" msgid="4323199171790522574">"Sí, converteix-lo en administrador"</string> + <string name="not_grant_admin" msgid="3557849576157702485">"No, no el converteixis en administrador"</string> <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Surt"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Desar l\'activitat de convidat?"</string> <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Pots desar l\'activitat de la sessió actual o suprimir totes les apps i dades"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 8da3a51d26ed..363c22e72aaf 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -545,13 +545,16 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Právě teď"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tento telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tento tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok s reproduktorem"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Na tomto zařízení média přehrávat nelze"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Pokud chcete přejít, upgradujte účet"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V zařízení nelze přehrávat"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Účet je třeba upgradovat"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Stažený obsah zde nelze přehrát"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Zkuste to znovu po reklamě"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Pokud zde chcete přehrávat média, probuďte zařízení"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Zařízení není schváleno k přehrávání"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Zařízení je třeba probudit"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Není schváleno k přehrávání"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Tato média zde přehrát nelze"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 22b72400a63d..1df4deeeddd2 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -117,7 +117,7 @@ <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Høreapparater"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-lyd"</string> <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Forbundet til høreapparater"</string> - <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Forbundet med LE Audio"</string> + <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Forbundet med LE-lyd"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Forbundet til medielyd"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Forbundet til telefonlyd"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Forbundet til filoverførselsserver"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Lige nu"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Denne telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Denne tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockhøjttaler"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhed"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Forbundet enhed"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke afspilles på denne enhed"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Opgrader kontoen for at skifte"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 57376ab7adfc..4c6f0c1c4b82 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -114,7 +114,7 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-Audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hörhilfen"</string> + <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hörgeräte"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Mit Hörhilfen verbunden"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Mit LE Audio verbunden"</string> @@ -545,6 +545,12 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Gerade eben"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Dieses Smartphone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Dieses Tablet"</string> + <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> + <skip /> + <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> + <skip /> + <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> + <skip /> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Wiedergabe auf diesem Gerät nicht möglich"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Zum Umstellen Kontoupgrade durchführen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 3b534a693a43..8f7518012def 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Μόλις τώρα"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Αυτό το τηλέφωνο"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Αυτό το tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Ηχείο βάσης σύνδεσης"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφ."</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Δεν είναι δυνατή η αναπαραγωγή σε αυτήν τη συσκευή"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Αναβαθμίστε τον λογαριασμό για εναλλαγή"</string> @@ -575,13 +578,13 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Χρήστης"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Προφίλ περιορ. πρόσβασης"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Προσθήκη νέου χρήστη;"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Μπορείτε να μοιραστείτε αυτήν τη συσκευή με άλλα άτομα, δημιουργώντας επιπλέον χρήστες. Κάθε χρήστης θα έχει το δικό του χώρο, τον οποίο μπορεί να προσαρμόσει με τις δικές του εφαρμογές, ταπετσαρία κ.λπ. Οι χρήστες μπορούν επίσης να προσαρμόσουν ρυθμίσεις της συσκευής, όπως το Wi‑Fi, που επηρεάζουν τους πάντες.\n\nΚατά την προσθήκη ενός νέου χρήστη, αυτός θα πρέπει να ρυθμίσει τον χώρο του.\n\nΟποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες. Οι ρυθμίσεις και οι υπηρεσίες προσβασιμότητας ενδέχεται να μην μεταφερθούν στον νέο χρήστη."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"Κατά την προσθήκη ενός νέου χρήστη, αυτός θα πρέπει να ρυθμίσει το χώρο του.\n\nΟποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Μπορείτε να μοιραστείτε αυτήν τη συσκευή με άλλα άτομα, δημιουργώντας επιπλέον χρήστες. Κάθε χρήστης θα έχει τον δικό του χώρο, τον οποίο μπορεί να προσαρμόσει με τις δικές του εφαρμογές, ταπετσαρία κ.λπ. Οι χρήστες μπορούν επίσης να προσαρμόσουν ρυθμίσεις της συσκευής, όπως το Wi‑Fi, που επηρεάζουν τους πάντες.\n\nΚατά την προσθήκη ενός νέου χρήστη, αυτός θα πρέπει να ρυθμίσει τον χώρο του.\n\nΟποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες. Οι ρυθμίσεις και οι υπηρεσίες προσβασιμότητας ενδέχεται να μην μεταφερθούν στον νέο χρήστη."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"Κατά την προσθήκη ενός νέου χρήστη, αυτός θα πρέπει να ρυθμίσει τον χώρο του.\n\nΟποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Να εκχωρηθούν δικαιώματα διαχειριστή σε αυτόν τον χρήστη;"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Οι διαχειριστές έχουν ειδικά προνόμια που δεν έχουν οι υπόλοιποι χρήστες Ένας διαχειριστής μπορεί να διαχειριστεί όλους τους χρήστες, να ενημερώσει ή να επαναφέρει αυτήν τη συσκευή, να τροποποιήσει τις ρυθμίσεις, να δει όλες τις εγκατεστημένες εφαρμογές και να εκχωρήσει ή να ανακαλέσει προνόμια διαχειριστή άλλων χρηστών."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Εκχώρηση δικαιωμάτων διαχειριστή"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Να γίνει ρύθμιση χρήστη τώρα;"</string> - <string name="user_setup_dialog_message" msgid="269931619868102841">"Βεβαιωθείτε ότι ο χρήστης μπορεί να πάρει τη συσκευή και ρυθμίστε το χώρο του"</string> + <string name="user_setup_dialog_message" msgid="269931619868102841">"Βεβαιωθείτε ότι ο χρήστης μπορεί να πάρει τη συσκευή και ρυθμίστε τον χώρο του"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Να γίνει ρύθμιση προφίλ τώρα;"</string> <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Ρύθμιση τώρα"</string> <string name="user_setup_button_setup_later" msgid="8712980133555493516">"Όχι τώρα"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 8e0aa4eefa4e..3a50fa331696 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 4066992d53f6..9ee30e11a404 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Cant play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 8e0aa4eefa4e..3a50fa331696 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 8e0aa4eefa4e..3a50fa331696 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index bd6ffa045fbc..9ad8a393f469 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Just now"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"This phone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"This tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dock speaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Cant play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index efd0a5ce53bc..4e6cd004268f 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -99,9 +99,9 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"I: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activado"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo; solo izquierda"</string> - <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo; solo derecha"</string> - <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activo; izquierda y derecha"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Activo; solo oído izquierdo"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Activo; solo oído derecho"</string> + <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Activo; oídos izquierdo y derecho"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimedia"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Llamadas telefónicas"</string> <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Recién"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Conector de la bocina"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 0d828bb938d6..6ffed25cb768 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -545,8 +545,11 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"justo ahora"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altavoz base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir contenido en este dispositivo"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"No se pueden reproducir descargas aquí"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Prueba de nuevo después del anuncio"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 9ba33bbec348..434e7d712cdc 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Äsja"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"See telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"See tahvelarvuti"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doki kõlar"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Väline seade"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ühendatud seade"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Selles seadmes ei saa esitada"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Lülitamiseks täiendage kontot"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 000507838433..591d3cf1e52c 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -99,8 +99,8 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Ezk. gailuaren bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Esk- gailuaren bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktibo"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, ezkerreko audifonoa soilik"</string> - <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, eskuineko audifonoa soilik"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktibo, ezkerrekoa soilik"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktibo, eskuinekoa soilik"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktibo, ezkerreko eta eskuineko audifonoak"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Euskarriaren audioa"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefono-deiak"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Oraintxe"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Telefono hau"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tableta hau"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Oinarri bozgorailuduna"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kanpoko gailua"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Konektatutako gailua"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ezin da erreproduzitu gailu honetan"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Aldatzeko, bertsio-berritu kontua"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 9f3359225cb9..b4e9b350432f 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -231,9 +231,9 @@ <string name="adb_wireless_error" msgid="721958772149779856">"خطا"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"اشکالزدایی بیسیم"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"برای مشاهده و استفاده از دستگاههای در دسترس، اشکالزدایی بیسیم را روشن کنید"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"مرتبط کردن دستگاه با رمزینه پاسخسریع"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"جفت کردن دستگاه با رمزینه پاسخسریع"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"دستگاههای جدید را بااستفاده از اسکنر رمزینه پاسخسریع مرتبط کنید"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"مرتبط کردن دستگاه با کد مرتبطسازی"</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"جفت کردن دستگاه با کد مرتبطسازی"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"دستگاههای جدید را با استفاده از کد شش رقمی مرتبط کنید"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"دستگاههای مرتبطشده"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"درحالحاضر متصل"</string> @@ -242,13 +242,13 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"اثرانگشت دستگاه: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"اتصال ناموفق"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"مطمئن شوید که <xliff:g id="DEVICE_NAME">%1$s</xliff:g> به شبکه درستی متصل شده باشد"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"مرتبط کردن با دستگاه"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"جفت کردن با دستگاه"</string> <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"کد مرتبطسازی Wi‑Fi"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"مرتبطسازی ناموفق"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"مطمئن شوید که دستگاه به همان شبکه متصل باشد."</string> <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"مرتبطسازی دستگاه…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"مرتبط کردن دستگاه انجام نشد. یا رمزینه پاسخسریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"جفت کردن دستگاه انجام نشد. یا رمزینه پاسخسریع اشتباه بوده است، یا دستگاه به همان شبکه متصل نیست."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"نشانی IP و درگاه"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"اسکن رمزینه پاسخسریع"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"دستگاه را ازطریق Wi‑Fi و با اسکن کردن رمزینه پاسخسریع مرتبط کنید"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"هماکنون"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"این تلفن"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"این رایانه لوحی"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"بلندگوی پایه اتصال"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"دستگاه متصل"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"نمیتوان در این دستگاه پخش کرد"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"برای تغییر، حساب را ارتقا دهید"</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"کاربر"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"نمایه محدود شده"</string> <string name="user_add_user_title" msgid="5457079143694924885">"کاربر جدیدی اضافه میکنید؟"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"با ایجاد کاربران بیشتر، میتوانید این دستگاه را با دیگران بهاشتراک بگذارید. هر کاربر فضای مخصوص به خودش را دارد که میتواند آن را با برنامهها، کاغذدیواری و موارد دیگر سفارشی کند. همچنین کاربران میتوانند تنظیماتی در دستگاه ایجاد کنند، مانند تنظیمات Wi-Fi، که بر تنظیمات بقیه اثر دارد.\n\nوقتی کاربر جدیدی اضافه میکنید، آن شخص باید فضای خودش را تنظیم کند.\n\nهر کاربر میتواند برنامهها را برای سایر کاربران بهروزرسانی کند. دسترسپذیری، تنظیمات، و سرویسها قابلانتقال به کاربر جدید نیستند."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"با ایجاد کاربران بیشتر، میتوانید از این دستگاه مشترکاً استفاده کنید. هر کاربر فضای مخصوص به خودش را دارد که میتواند آن را با برنامهها، کاغذدیواری و موارد دیگر سفارشی کند. علاوهبراین کاربران میتوانند تنظیماتی در دستگاه ایجاد کنند، مانند تنظیمات Wi-Fi، که بر تنظیمات بقیه اثر دارد.\n\nوقتی کاربر جدیدی اضافه میکنید، آن شخص باید فضای خودش را تنظیم کند.\n\nهر کاربر میتواند برنامهها را برای سایر کاربران بهروزرسانی کند. دسترسپذیری، تنظیمات، و سرویسها قابلانتقال به کاربر جدید نیستند."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"وقتی کاربر جدیدی اضافه میکنید آن فرد باید فضای خودش را تنظیم کند.\n\nهر کاربری میتواند برنامهها را برای همه کاربران دیگر بهروزرسانی کند."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"این کاربر سرپرست شود؟"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"سرپرستان امتیازهای ویژهای دارند که کاربران دیگر ندارند. سرپرست میتواند همه کاربران را مدیریت کند، این دستگاه را بهروز یا بازنشانی کند، تنظیمات را تغییر دهد، همه برنامههای نصبشده را ببیند، و امتیازهای سرپرست را به دیگران اعطا کند یا از آنها بگیرد."</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index a543f360a5c3..b3a7560e128c 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Äsken"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tämä puhelin"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tämä tabletti"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Telinekaiutin"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Yhdistetty laite"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ei voi toistaa tällä laitteella"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Vaihda päivittämällä tili"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 94dbeef13280..8d3eae4003ab 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -86,7 +86,7 @@ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Déconnexion…"</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Connexion en cours…"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Connecté à <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"Association…"</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"Association en cours…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connecté (aucun téléphone) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connecté (aucun média) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connecté (aucun téléphone ni média) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -99,7 +99,7 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"G : charge à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; D : charge à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Actif"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Active, gauche seulement"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Actif, gauche seulement"</string> <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Active, droite seulement"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Active, gauche et droite"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Paramètres audio du support"</string> @@ -520,7 +520,7 @@ <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Plus longtemps."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Moins longtemps."</string> <string name="cancel" msgid="5665114069455378395">"Annuler"</string> - <string name="next" msgid="2699398661093607009">"Suivante"</string> + <string name="next" msgid="2699398661093607009">"Suivant"</string> <string name="back" msgid="5554327870352703710">"Retour"</string> <string name="save" msgid="3745809743277153149">"Enregistrer"</string> <string name="okay" msgid="949938843324579502">"OK"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Cette tablette"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur du socle"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de faire jouer le contenu sur cet appareil"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à jour le compte pour passer à la version payante"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 5a492a8785bd..a4e43c7cb0ef 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -545,12 +545,15 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"À l\'instant"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ce téléphone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Cette tablette"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Haut-parleur station d\'accueil"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de lire du contenu sur cet appareil"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à niveau le compte pour changer"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Impossible de lire les téléchargements ici"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Réessayez après l\'annonce"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activer l\'appareil pour lire du contenu ici"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Activez l\'appareil pour lire du contenu ici"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Appareil non autorisé à lire du contenu"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Impossible de lire ce contenu multimédia ici"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 9ea4cbd2396a..dbc0ba618892 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este teléfono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Esta tableta"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altofalante da base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Non se pode reproducir contido neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Cambia a conta a un plan superior para facer a modificación"</string> @@ -588,7 +591,7 @@ <string name="user_add_user_type_title" msgid="551279664052914497">"Engadir"</string> <string name="user_new_user_name" msgid="60979820612818840">"Novo usuario"</string> <string name="user_new_profile_name" msgid="2405500423304678841">"Novo perfil"</string> - <string name="user_info_settings_title" msgid="6351390762733279907">"Información usuario"</string> + <string name="user_info_settings_title" msgid="6351390762733279907">"Información do usuario"</string> <string name="profile_info_settings_title" msgid="105699672534365099">"Información do perfil"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restrinxido, precisarás configurar un bloqueo da pantalla para protexer as túas aplicacións e datos persoais."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 04179400c27a..ee44c0aa4a53 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -86,7 +86,7 @@ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"ડિસ્કનેક્ટ થઈ રહ્યું છે..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"કનેક્ટ થઈ રહ્યું છે…"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"જોડી કરી રહ્યું છે…"</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"જોડાણ કરી રહ્યાં છીએ…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (કોઈ ફોન નથી)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (કોઈ મીડિયા નથી)"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (ફોન કે મીડિયા નથી)"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"હમણાં જ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"આ ફોન"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"આ ટૅબ્લેટ"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ડૉક સ્પીકર"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"કનેક્ટ કરેલું ડિવાઇસ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"આ ડિવાઇસ પર ચલાવી શકતા નથી"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"સ્વિચ કરવા માટે એકાઉન્ટ અપગ્રેડ કરો"</string> @@ -576,7 +579,7 @@ <string name="user_add_profile_item_title" msgid="3111051717414643029">"પ્રતિબંધિત પ્રોફાઇલ"</string> <string name="user_add_user_title" msgid="5457079143694924885">"નવા વપરાશકર્તાને ઉમેરીએ?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"તમે વધારાના વપરાશકર્તાઓ બનાવીને અન્ય લોકો સાથે આ ડિવાઇસને શેર કરી શકો છો. દરેક વપરાશકર્તા પાસે તેમની પોતાની સ્પેસ છે, જેને તેઓ ઍપ, વૉલપેપર, વગેરે સાથે કસ્ટમાઇઝ કરી શકે છે. વપરાશકર્તાઓ પ્રત્યેક વ્યક્તિને અસર કરતી હોય તેવી ડિવાઇસ સેટિંગ જેમ કે વાઇ-ફાઇને પણ સમાયોજિત કરી શકે છે.\n\nજ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમની સ્પેસ સેટ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા અન્ય બધા વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે. નવા વપરાશકર્તાને ઍક્સેસિબિલિટી સેટિંગ અને સેવાઓ ટ્રાન્સફર ન પણ થાય."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"જ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમનું સ્થાન સેટ અપ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા બધા અન્ય વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"જ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમની સ્પેસ સેટ અપ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા બધા અન્ય વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"આ વપરાશકર્તાને ઍડમિન બનાવવા છે?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"ઍડમિન પાસે વિશિષ્ટ વિશેષાધિકારો હોય છે જે અન્ય વપરાશકર્તાઓ પાસે હોતા નથી. ઍડમિન બધા વપરાશકર્તાઓને મેનેજ કરી શકે, આ ડિવાઇસને અપડેટ અથવા રીસેટ કરી શકે, સેટિંગમાં ફેરફાર કરી શકે, ઇન્સ્ટૉલ કરેલી બધી ઍપ જોઈ શકે અને અન્ય લોકોને ઍડમિનના અધિકારો આપી શકે અથવા તેમને રદબાતલ કરી શકે."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"ઍડમિન બનાવો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 3d048456684a..b97837bf9eba 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"अभी-अभी"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यह फ़ोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यह टैबलेट"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट किया गया डिवाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"इस डिवाइस पर मीडिया नहीं चलाया जा सकता"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"प्रीमियम खाते में स्विच करने के लिए, अपना खाता अपग्रेड करें"</string> @@ -578,7 +581,7 @@ <string name="user_add_user_message_long" msgid="1527434966294733380">"नए उपयोगकर्ता जोड़कर इस डिवाइस को दूसरे लोगों के साथ शेयर किया जा सकता है. हर उपयोगकर्ता के पास अपनी जगह होती है, जिसमें वे ऐप्लिकेशन, वॉलपेपर, और दूसरी चीज़ों में मनमुताबिक बदलाव कर सकते हैं. उपयोगकर्ता, वाई-फ़ाई जैसी डिवाइस सेटिंग में भी बदलाव कर सकते हैं. इसका असर हर किसी पर पड़ता है.\n\nजब किसी नए उपयोगकर्ता को जोड़ा जाता है, तो उसे अपनी जगह सेट अप करनी होती है.\n\nकोई भी उपयोगकर्ता, दूसरे सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है. ऐसा भी हो सकता है कि सुलभता सेटिंग और सेवाएं नए उपयोगकर्ता को ट्रांसफ़र न हो पाएं."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"कोई नया उपयोगकर्ता जोड़ने पर, उसे अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता, बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"क्या इस व्यक्ति को एडमिन बनाना है?"</string> - <string name="user_grant_admin_message" msgid="1673791931033486709">"एडमिन, अन्य लोगों के मुकाबले खास अधिकार होते हैं. एडमिन के पास ये अधिकार होते हैं: सभी लोगों को मैनेज करना, इस डिवाइस को अपडेट या रीसेट करना, सेटिंग में बदलाव करना, इंस्टॉल किए गए सभी ऐप्लिकेशन देखना, और अन्य लोगों को एडमिन के खास अधिकार देना या उन्हें वापस लेना."</string> + <string name="user_grant_admin_message" msgid="1673791931033486709">"एडमिन के पास अन्य लोगों के मुकाबले खास अधिकार होते हैं. एडमिन के पास ये अधिकार होते हैं: सभी लोगों को मैनेज करना, इस डिवाइस को अपडेट या रीसेट करना, सेटिंग में बदलाव करना, इंस्टॉल किए गए सभी ऐप्लिकेशन देखना, और अन्य लोगों को एडमिन के खास अधिकार देना या उन्हें वापस लेना."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"एडमिन बनाएं"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"उपयोगकर्ता को अभी सेट करें?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"पक्का करें कि व्यक्ति डिवाइस का इस्तेमाल करने और अपनी जगह सेट करने के लिए मौजूद है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 8649e22c6d22..6acfb9545c7b 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Upravo sad"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ovaj telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ovaj tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvučnik priključne stanice"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nije moguće reproducirati na ovom uređaju"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite račun radi prebacivanja"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Preuzimanja se ne mogu reproducirati ovdje"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne može se reproducirati ovdje"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite i prebacite se"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ne može se tu reproducirati"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Pokušajte ponovo nakon oglasa"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Aktivirajte uređaj da biste na njemu reproducirali"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Uređaj nije odobren za reprodukciju"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Ti se mediji ne mogu reproducirati ovdje"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Aktivirajte i reproducirajte ovdje"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Nije odobreno za reprodukciju"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Nemoguća je reprodukcija medija"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 0b9737e137cf..14ecad0339f0 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Az imént"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ez a telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ez a táblagép"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhangszóró"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Külső eszköz"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Csatlakoztatott eszköz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nem lehet tartalmat lejátszani ezen az eszközön"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"A váltáshoz frissítse fiókját magasabb kategóriára"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Itt nem lehet lejátszani a letöltött elemeket"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nem játszható le ezen az eszközön"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"A váltáshoz frissítse fiókját"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Itt nem játszhatók le a letöltések"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Próbálja újra a hirdetés után"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"A tartalom itteni lejátszásához ébressze fel az eszközt"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Az eszköz nem játszhat le tartalmat"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"A médiatartalom nem játszható le itt"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Lejátszáshoz ébressze fel az eszközt"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Az eszköz nem játszhat le"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"A tartalom nem játszható le itt"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string> <string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 55bb12241789..5083165e5fa7 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Հենց նոր"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Այս հեռախոսը"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Այս պլանշետը"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Դոկ-կայանով բարձրախոս"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Միացված սարք"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Հնարավոր չէ նվագարկել այս սարքում"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Փոխելու համար անցեք հաշվի պրեմիում տարբերակին"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index b6756c2beeca..c5e69126b0b3 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Baru saja"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ponsel ini"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tablet ini"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker dok"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Perangkat Eksternal"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Perangkat yang terhubung"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat memutar di perangkat ini"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade akun untuk beralih"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index db1cd0feb8c5..5d1893088281 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Rétt í þessu"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Þessi sími"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Þessi spjaldtölva"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Hátalaradokka"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ytra tæki"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tengt tæki"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ekki er hægt að spila í þessu tæki"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppfærðu reikninginn til að skipta"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 9c28102c5ae8..85d4985cc50e 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -86,7 +86,7 @@ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connesso"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento in corso…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono o media esclusi)"</string> @@ -99,8 +99,8 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"S: batteria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: batteria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Attivo"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Attivo, solo sinistra"</string> - <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Attivo, solo destra"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Attiva, solo sinistra"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Attiva, solo destra"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Attivo, destra e sinistra"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Audio multimediale"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Telefonate"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Adesso"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Questo telefono"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Questo tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Base con altoparlante"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo telefono"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Utente"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Profilo con limitazioni"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Aggiungere un nuovo utente?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Puoi condividere il dispositivo con altre persone creando altri utenti. Ogni utente ha un proprio spazio personalizzabile con app, sfondo e così via. Gli utenti possono anche regolare le impostazioni del dispositivo, come il Wi‑Fi, che riguardano tutti.\n\nQuando crei un nuovo utente, la persona in questione deve configurare il proprio spazio.\n\nQualsiasi utente può aggiornare le app per tutti gli altri utenti. I servizi e le impostazioni di accessibilità non potranno essere trasferiti al nuovo utente."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Puoi condividere il dispositivo con altre persone creando altri utenti. Ogni utente ha un proprio spazio personalizzabile con app, sfondo e così via. Gli utenti possono anche regolare le impostazioni del dispositivo, come il Wi‑Fi, che riguardano tutti.\n\nQuando crei un nuovo utente, la persona in questione deve configurare il proprio spazio.\n\nQualsiasi utente può aggiornare le app per tutti gli altri utenti. È possibile che i servizi e le impostazioni di accessibilità non vengano trasferiti al nuovo utente."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Il nuovo utente, una volta aggiunto, deve configurare il proprio spazio.\n\nQualsiasi utente può aggiornare le app per tutti gli altri."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Vuoi impostare questo utente come amministratore?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Gli amministratori hanno privilegi speciali che altri utenti non hanno. Un amministratore può gestire tutti gli utenti, aggiornare o resettare questo dispositivo, modificare le impostazioni, vedere tutte le app installate e concedere o revocare privilegi amministrativi per altri utenti."</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 167be607049b..af56031e7901 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"הרגע"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"הטלפון הזה"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"הטאבלט הזה"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"הרמקול של אביזר העגינה"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר הזה"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"צריך לשדרג את החשבון כדי לעבור"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"לא ניתן להפעיל את ההורדות כאן"</string> - <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"אפשר לנסות שוב לאחר המודעה"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"צריך להוציא את המכשיר ממצב השינה כדי להפעיל כאן"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"המכשיר לא קיבל אישור להפעלה"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"אי אפשר להפעיל את המדיה הזו כאן"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"יש לשדרג חשבון כדי לעבור"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"לא ניתן להפעיל הורדות"</string> + <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"יש לנסות אחרי המודעה"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"צריך להעיר את המכשיר"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"המכשיר לא אושר"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"אי אפשר להפעיל מדיה"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string> <string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"משתמש"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"פרופיל מוגבל"</string> <string name="user_add_user_title" msgid="5457079143694924885">"להוסיף משתמש חדש?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"ניתן לשתף מכשיר זה עם אנשים אחרים על ידי יצירת משתמשים נוספים. לכל משתמש מרחב משלו, שאותו אפשר להתאים אישית בעזרת אפליקציות, טפט ופריטים נוספים. המשתמשים יכולים גם להתאים הגדרות של המכשיר כגון Wi‑Fi, שמשפיעות על כולם.\n\nכשמוסיפים משתמש חדש, על משתמש זה להגדיר את המרחב שלו.\n\nכל אחד מהמשתמשים יכול לעדכן אפליקציות לכל שאר המשתמשים. ייתכן שהגדרות ושירותים של נגישות לא יועברו למשתמש החדש."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"ניתן לשתף את המכשיר הזה עם אנשים אחרים על ידי יצירת משתמשים נוספים. לכל משתמש מרחב משלו, שאותו אפשר להתאים אישית בעזרת אפליקציות, טפט ופריטים נוספים. המשתמשים יכולים גם להתאים הגדרות של המכשיר כמו Wi‑Fi, שמשפיעות על כולם.\n\nכשמוסיפים משתמש חדש, המשתמש הזה צריך להגדיר את המרחב שלו.\n\nכל אחד מהמשתמשים יכול לעדכן אפליקציות לכל שאר המשתמשים. יכול להיות שהגדרות ושירותים של נגישות לא יועברו למשתמש החדש."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"כשמוסיפים משתמש חדש, הוא צריך להגדיר את המרחב שלו.\n\nכל משתמש יכול לעדכן אפליקציות עבור כל המשתמשים האחרים."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"להגדיר את המשתמש הזה כאדמין?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"לאדמינים יש הרשאות מיוחדות שאין למשתמשים אחרים. אדמין יכול לנהל את כל המשתמשים, לעדכן את המכשיר הזה או לאפס אותו, לשנות הגדרות, לראות את כל האפליקציות המותקנות ולהעניק הרשאות אדמין לאחרים או לשלול אותן."</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 032046811603..076d0223df9c 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -543,16 +543,22 @@ <string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"毎回確認"</string> <string name="zen_mode_forever" msgid="3339224497605461291">"OFF にするまで"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"たった今"</string> - <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"このスマートフォン"</string> + <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"このデバイス"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"このタブレット"</string> - <string name="media_transfer_this_phone" msgid="7194341457812151531">"このスマートフォン"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"このデバイスで再生できません"</string> + <!-- no translation found for media_transfer_dock_speaker_device_name (2856219597113881950) --> + <skip /> + <!-- no translation found for media_transfer_external_device_name (2588672258721846418) --> + <skip /> + <!-- no translation found for media_transfer_default_device_name (4315604017399871828) --> + <skip /> + <string name="media_transfer_this_phone" msgid="7194341457812151531">"このデバイス"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"このデバイスでは再生できません"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"アカウントを更新して切り替えてください"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ダウンロードしたコンテンツをここでは再生できません"</string> - <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"広告が表示されてから、もう一度試してください"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ここで再生するにはデバイスを起動してください"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"このデバイスでの再生が許可されていません"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"このメディアはここで再生できません"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"再生不可: ダウンロードしたコンテンツ"</string> + <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"広告の後にもう一度試してください"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"デバイスの起動が必要です"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"再生が許可されていません"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"このメディアは再生できません"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string> <string name="help_label" msgid="3528360748637781274">"ヘルプとフィードバック"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 115260966a67..147512b2cc20 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -545,10 +545,13 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ახლახან"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ეს ტელეფონი"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ეს ტაბლეტი"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"დინამიკის სამაგრი"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"დაკავშირებული მოწყობილობა"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ამ მოწყობილობაზე დაკვრა შეუძლებელია"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"გადასართავად განაახლეთ ანგარიში"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"აქ ჩამოტვირთვების თამაში შეუძლებელია"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"შეუძლებელია აქ ჩამოტვირ. თამაში"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"სცადეთ ხელახლა რეკლამის შემდეგ"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"დასაკრავად გამოაღვიძეთ ტელეფონი"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"მოწყობილობა არ არის ავტორიზებული დასაკრავად"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 996d216ede14..7df9fb329b43 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Ажыратылған"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ажыратылуда…"</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Жалғауда..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Қосылып жатыр..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Жұптасып жатыр..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды (телефонсыз)"</string> @@ -545,12 +545,15 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Дәл қазір"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Осы телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Осы планшет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Қондыру динамигі"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Жалғанған құрылғы"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Осы құрылғыда ойнату мүмкін емес."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Ауысу үшін аккаунтты жаңартыңыз."</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктеп алынғандарды осы жерде ойнату мүмкін емес."</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Жарнамадан кейін қайталап көріңіз."</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Осы жерде ойнату үшін құрылғыны оятыңыз."</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Осы жерде ойнату үшін құрылғыны ұйқы режимінен шығарыңыз."</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Ойнату үшін авторизация керек."</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Бұл мультимедиа файлын осы жерде ойнату мүмкін емес."</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Пайдаланушы"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Шектелген профайл"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Жаңа пайдаланушы қосылсын ба?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Қосымша профильдер жасай отырып, бұл құрылғыны басқалармен ортақ пайдалануға болады. Әр пайдаланушы қолданбаларды, тұсқағаздарды орнатып, профилін өз қалауынша реттей алады. Сондай-ақ барлығы ортақ қолданатын Wi‑Fi сияқты параметрлерді де реттеуге болады.\n\nЖаңа пайдаланушы енгізілгенде, ол өз профилін реттеуі керек болады.\n\nКез келген пайдаланушы барлық басқа пайдаланушылар үшін қолданбаларды жаңарта алады. Арнайы мүмкіндіктерге қатысты параметрлер мен қызметтер жаңа пайдаланушыға өтпейді."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Қосымша пайдаланушылар жасай отырып, бұл құрылғыны басқалармен бөлісуге болады. Әр пайдаланушы қолданбалар, тұсқағаздар орнатып, профилін қалауынша реттей алады. Барлық пайдаланушы қолданатын Wi‑Fi сияқты параметрлерді де реттеуге болады.\n\nЖаңа пайдаланушы қосылғанда, ол өз профилін реттеуі керек.\n\nКез келген пайдаланушы басқа пайдаланушылар үшін қолданбаларды жаңарта алады. Арнайы мүмкіндіктерге қатысты параметрлер мен қызметтер жаңа пайдаланушыға берілмейді."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Жаңадан қосылған пайдаланушы өз профилін реттеуі керек.\n\nКез келген пайдаланушы басқалар үшін қолданбаларды жаңарта алады."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Осы пайдаланушыны әкімші ету керек пе?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Әкімшілер басқа пайдаланушыларда болмайтын арнайы өкілеттерге ие. Әкімші мына әрекеттерді орындай алады: барлық пайдаланушыны басқару, осы құрылғыны жаңарту не бастапқы күйге қайтару, параметрлерді өзгерту, орнатылған құрылғының барлығын көру және әкімші өкілеттерін басқа пайдаланушыларға беру не қайтару."</string> @@ -611,8 +614,8 @@ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Мұндайда жаңа қонақ сеансы басталады және ағымдағы сеанстағы барлық қолданба мен дерек жойылады."</string> <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Қонақ режимінен шығу керек пе?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ағымдағы қонақ сеансындағы барлық қолданба мен дерек жойылады."</string> - <string name="grant_admin" msgid="4323199171790522574">"Иә, пайдаланушы әкімші етілсін"</string> - <string name="not_grant_admin" msgid="3557849576157702485">"Жоқ, пайдаланушы әкімші етілмесін"</string> + <string name="grant_admin" msgid="4323199171790522574">"Иә, әкімші болсын"</string> + <string name="not_grant_admin" msgid="3557849576157702485">"Жоқ, әкімші болмасын"</string> <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Шығу"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Қонақ әрекетін сақтау керек пе?"</string> <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ағымдағы сеанс тарихын сақтауға не барлық қолданба мен деректі жоюға болады."</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index e546821e1adc..81d26692a20e 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -99,7 +99,7 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"សកម្ម"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"សកម្មខាងឆ្វេងតែប៉ុណ្ណោះ"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"សកម្ម ខាងឆ្វេងតែប៉ុណ្ណោះ"</string> <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"សកម្មខាងស្ដាំតែប៉ុណ្ណោះ"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"សកម្មខាងឆ្វេង និងស្ដាំ"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"សំឡេងមេឌៀ"</string> @@ -545,13 +545,16 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"អម្បាញ់មិញ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ទូរសព្ទនេះ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ថេប្លេតនេះ"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ឧបករណ៍បំពងសំឡេងដែលមានជើងភ្ជាប់"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ឧបករណ៍ដែលបានភ្ជាប់"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ទូរសព្ទនេះ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"មិនអាចចាក់នៅលើឧបករណ៍នេះបានទេ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ដំឡើងកម្រិតគណនី ដើម្បីប្ដូរ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"មិនអាចចាក់ខ្លឹមសារដែលបានទាញយកនៅទីនេះបានទេ"</string> - <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"ព្យាយាមម្ដងទៀត បន្ទាប់ពីការផ្សាយពាណិជ្ជកម្ម"</string> + <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"ព្យាយាមម្ដងទៀត បន្ទាប់ពីពាណិជ្ជកម្ម"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ដាស់ឧបករណ៍ឱ្យចាក់នៅទីនេះ"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ឧបករណ៍មិនយល់ព្រមឱ្យចាក់ទេ"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ឧបករណ៍មិនព្រមឱ្យចាក់ទេ"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"មិនអាចចាក់មេឌៀនេះនៅទីនេះបានទេ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មានបញ្ហាក្នុងការភ្ជាប់។ បិទ រួចបើកឧបករណ៍វិញ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍សំឡេងប្រើខ្សែ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index f4802a65fd38..f7f6a2f22ce2 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -49,7 +49,7 @@ <string name="wifi_security_none_owe" msgid="5241745828327404101">"ಯಾವುದೂ ಇಲ್ಲ/Enhanced Open"</string> <string name="wifi_security_owe" msgid="3343421403561657809">"Enhanced Open"</string> <string name="wifi_security_eap_suiteb" msgid="415842785991698142">"WPA3-ಎಂಟರ್ಪ್ರೈಸ್ 192-ಬಿಟ್"</string> - <string name="wifi_remembered" msgid="3266709779723179188">"ಉಳಿಸಲಾಗಿದೆ"</string> + <string name="wifi_remembered" msgid="3266709779723179188">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="wifi_disconnected" msgid="7054450256284661757">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string> @@ -545,13 +545,16 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ಇದೀಗ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ಈ ಫೋನ್"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ಈ ಟ್ಯಾಬ್ಲೆಟ್"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ಡಾಕ್ ಸ್ಪೀಕರ್"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ಕನೆಕ್ಟ್ ಮಾಡಿರುವ ಸಾಧನ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ಬದಲಾಯಿಸಲು ಖಾತೆಯನ್ನು ಅಪ್ಗ್ರೇಡ್ ಮಾಡಿ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ಇಲ್ಲಿ ಡೌನ್ಲೋಡ್ಗಳನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> - <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"ಜಾಹೀರಾತಿನ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string> + <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"ಆ್ಯಡ್ನ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ಇಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧನವನ್ನು ಎಚ್ಚರಿಸಿ"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ಸಾಧನವನ್ನು ಪ್ಲೇ ಮಾಡಲು ಅನುಮೋದಿಸಲಾಗಿಲ್ಲ"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ಪ್ಲೇ ಮಾಡಲು ಸಾಧನವನ್ನು ಅನುಮೋದಿಸಲಾಗಿಲ್ಲ"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ಈ ಮಾಧ್ಯಮವನ್ನು ಇಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string> @@ -574,9 +577,9 @@ <string name="user_add_profile_item_summary" msgid="5418602404308968028">"ನಿಮ್ಮ ಖಾತೆಯಿಂದ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವಿಷಯಕ್ಕೆ ಪ್ರವೇಶವನ್ನು ನೀವು ನಿರ್ಬಂಧಿಸಬಹುದು"</string> <string name="user_add_user_item_title" msgid="2394272381086965029">"ಬಳಕೆದಾರ"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"ನಿರ್ಬಂಧಿಸಿದ ಪ್ರೊಫೈಲ್"</string> - <string name="user_add_user_title" msgid="5457079143694924885">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸುವುದೇ?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"ನೀವು ಹೆಚ್ಚುವರಿ ಬಳಕೆದಾರರನ್ನು ರಚಿಸುವ ಮೂಲಕ ಇತರ ಜನರ ಜೊತೆಗೆ ಈ ಸಾಧನವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು. ಪ್ರತಿ ಬಳಕೆದಾರರು ತಮ್ಮದೇ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುತ್ತಾರೆ, ಇದರಲ್ಲಿ ಅವರು ತಮ್ಮದೇ ಅಪ್ಲಿಕೇಶನ್ಗಳು, ವಾಲ್ಪೇಪರ್ ಮತ್ತು ಮುಂತಾದವುಗಳ ಮೂಲಕ ಕಸ್ಟಮೈಸ್ ಮಾಡಿಕೊಳ್ಳಬಹುದು. ಎಲ್ಲರ ಮೇಲೂ ಪರಿಣಾಮ ಬೀರುವಂತೆ ವೈ-ಫೈ ರೀತಿಯ ಸಾಧನ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬಳಕೆದಾರರು ಸರಿಹೊಂದಿಸಬಹುದು.\n\nನೀವು ಒಬ್ಬ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ಅವರ ಸ್ಥಳವನ್ನು ಹೊಂದಿಸಬೇಕಾಗುತ್ತದೆ.\n\nಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗೆ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಸೇವೆಗಳು ಹೊಸ ಬಳಕೆದಾರರಿಗೆ ವರ್ಗಾವಣೆ ಆಗದಿರಬಹುದು."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"ನೀವು ಒಬ್ಬ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ಅವರ ಸ್ಥಳವನ್ನು ಸ್ಥಾಪಿಸಬೇಕಾಗುತ್ತದೆ.\n\nಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು."</string> + <string name="user_add_user_title" msgid="5457079143694924885">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಬೇಕೆ?"</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"ನೀವು ಹೆಚ್ಚುವರಿ ಬಳಕೆದಾರರನ್ನು ರಚಿಸುವ ಮೂಲಕ ಇತರ ಜನರ ಜೊತೆಗೆ ಈ ಸಾಧನವನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು. ಪ್ರತಿ ಬಳಕೆದಾರರು ತಮ್ಮದೇ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುತ್ತಾರೆ, ಇದರಲ್ಲಿ ಅವರು ತಮ್ಮದೇ ಅಪ್ಲಿಕೇಶನ್ಗಳು, ವಾಲ್ಪೇಪರ್ ಮತ್ತು ಮುಂತಾದವುಗಳ ಮೂಲಕ ಕಸ್ಟಮೈಸ್ ಮಾಡಿಕೊಳ್ಳಬಹುದು. ಎಲ್ಲರ ಮೇಲೂ ಪರಿಣಾಮ ಬೀರುವಂತೆ ವೈ-ಫೈ ರೀತಿಯ ಸಾಧನ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬಳಕೆದಾರರು ಸರಿಹೊಂದಿಸಬಹುದು.\n\nನೀವು ಒಬ್ಬ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ತಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಟಪ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ.\n\nಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗೆ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು. ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಸೇವೆಗಳು ಹೊಸ ಬಳಕೆದಾರರಿಗೆ ವರ್ಗಾವಣೆ ಆಗದಿರಬಹುದು."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"ನೀವು ಒಬ್ಬ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ತಮ್ಮ ಸ್ಥಳವನ್ನು ಸೆಟಪ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ.\n\nಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"ಈ ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಾಹಕರನ್ನಾಗಿ ಮಾಡಬೇಕೆ?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"ನಿರ್ವಾಹಕರು ಇತರ ಬಳಕೆದಾರರಿಗೆ ಇಲ್ಲದ ವಿಶೇಷ ಸೌಲಭ್ಯಗಳನ್ನು ಹೊಂದಿದ್ದಾರೆ. ನಿರ್ವಾಹಕರು ಎಲ್ಲಾ ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸಬಹುದು, ಈ ಸಾಧನವನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು ಅಥವಾ ರೀಸೆಟ್ ಮಾಡಬಹುದು, ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಹೊಂದಿಸಬಹುದು, ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾದ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಬಹುದು ಮತ್ತು ಇತರರಿಗೆ ನಿರ್ವಾಹಕರಿಗೆ ನೀಡಿರುವ ಸೌಲಭ್ಯಗಳನ್ನು ನೀಡಬಹುದು ಅಥವಾ ಹಿಂತೆಗೆದುಕೊಳ್ಳಬಹುದು."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"ನಿರ್ವಾಹಕರನ್ನಾಗಿ ಮಾಡಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 0eaf6dd171da..ffa05cc91bd1 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"조금 전"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"이 휴대전화"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"태블릿"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"도크 스피커"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"연결된 기기"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"이 휴대전화"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"이 기기에서 재생할 수 없음"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"전환하려면 계정을 업그레이드하세요."</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 6c9abb262f08..58a930f304de 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -99,7 +99,7 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"Батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Сол: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, оң: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Жигердүү"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Жигердүү, сол кулакчын гана"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Иштеп жатат, сол кулак гана"</string> <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Жигердүү, оң кулакчын гана"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Жигердүү, сол жана оң кулакчын"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Аудио"</string> @@ -141,7 +141,7 @@ <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Жок"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Жупташканда байланыштарыңыз менен чалуу таржымалыңызды пайдалана аласыз."</string> <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүнө туташуу мүмкүн болгон жок."</string> - <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"\"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" туташкан жок: PIN-код же сырсөз туура эмес."</string> + <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"\"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" туташкан жок: PIN код же сырсөз туура эмес."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> менен байланышуу мүмкүн эмес."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Жупташтырууну <xliff:g id="DEVICE_NAME">%1$s</xliff:g> четке какты."</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Компьютер"</string> @@ -231,7 +231,7 @@ <string name="adb_wireless_error" msgid="721958772149779856">"Ката"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"Мүчүлүштүктөрдү Wi-Fi аркылуу аныктоо"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Жеткиликтүү түзмөктөрдү көрүү үчүн мүчүлүштүктөрдү Wi-Fi аркылуу аныктоону күйгүзүңүз"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Түзмөктү QR коду аркылуу жупташтыруу"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Түзмөктү QR код аркылуу жупташтыруу"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR кодунун сканерин колдонуп, жаңы түзмөктөрдү жупташтырыңыз"</string> <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Түзмөктү атайын код аркылуу жупташтыруу"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Жаңы түзмөктөрдү алты сандан турган код аркылуу жупташтырасыз"</string> @@ -246,12 +246,12 @@ <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi аркылуу байланыштыруу коду"</string> <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Туташкан жок"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Түзмөк бир тармакка туташып турушу керек."</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодун скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR кодду скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Түзмөк жупташтырылууда…"</string> - <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Түзмөк жупташтырылган жок. QR коду туура эмес же түзмөк бир тармакка туташпай турат."</string> + <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Түзмөк жупташтырылган жок. QR код туура эмес же түзмөк бир тармакка туташпай турат."</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP дарек жана порт"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодун скандоо"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодун скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR кодду скандоо"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR кодду скандап, түзмөктү Wi‑Fi аркылуу жупташтырыңыз"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Wi-Fi тармагына туташыңыз"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, мүчүлүштүктөрдү оңдоо, иштеп чыгуу"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"Ката жөнүндө кабарлоо"</string> @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Жаңы эле"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ушул телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ушул планшет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док бекети үчүн динамик"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Туташкан түзмөк"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотуу мүмкүн эмес"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Которулуу үчүн аккаунтуңузду жаңыртыңыз"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктөлүп алынгандарды бул жерде ойнотуу мүмкүн эмес"</string> - <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Жарнамадан кийин кайталап көрүңүз"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Угуу үчүн түзмөктү уйку режиминен чыгарыңыз"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Бул түзмөктө ойнотуу үчүн уруксат алуу керек"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Бул медианы ойнотуу мүмкүн эмес"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотууга болбойт"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Премиум аккаунтка которулуу керек"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктөлүп алынгандар ойнотулбайт"</string> + <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Жарнамадан кийин кайталаңыз"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Угуу үчүн түзмөктү уйкудан чыгарыңыз"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Ойнотуу үчүн уруксат алышыңыз керек"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Медиа файлды ойното албайсыз"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string> <string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 1b7138db7172..28fd87e96c7d 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ຕອນນີ້"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ໂທລະສັບນີ້"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ແທັບເລັດນີ້"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ແທ່ນວາງລຳໂພງ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ອຸປະກອນທີ່ເຊື່ອມຕໍ່"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ຫຼິ້ນຢູ່ອຸປະກອນນີ້ບໍ່ໄດ້"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ອັບເກຣດບັນຊີເພື່ອສະຫຼັບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 6024772571db..8246c6bfd168 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Ką tik"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Šis telefonas"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Šis planšetinis kompiuteris"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doko garsiakalbis"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Išorinis įrenginys"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Prijungtas įrenginys"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Negalima leisti šiame įrenginyje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Jei norite perjungti, naujovinkite paskyrą"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 259e3f4e2580..807df3a3f91d 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Tikko"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Šis tālrunis"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Šis planšetdators"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Doka skaļrunis"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ārēja ierīce"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pievienotā ierīce"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nevar atskaņot šajā ierīcē."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Lai pārslēgtu, jauniniet kontu"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 55d29cd3a9b1..f7b4c0feed86 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Пред малку"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Овој телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Овој таблет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Док со звучник"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Поврзан уред"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не може да се пушти на уредов"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надградете ја сметката за да се префрлите"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Не може да се пуштаат преземања тука"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Обидете се повторно по рекламата"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Разбудете го уредот за да пуштате овде"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Разбудете го уредот за да пуштате тука"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Уредот не е одобрен за репродукција"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Не може да се пуштат аудиовизуелните содржини овде"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Овие аудиовизуелни содржини не може да се пуштат тука"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string> <string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string> @@ -577,9 +580,9 @@ <string name="user_add_user_title" msgid="5457079143694924885">"Да се додаде нов корисник?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Уредов може да го споделувате со други лица ако додадете дополнителни корисници. Секој корисник има сопствен простор што може да го приспособува со апликации, тапети и слично. Корисниците може да приспособуваат и поставки за уредот, како на пр., Wi‑Fi, што важат за сите.\n\nКога додавате нов корисник, тоа лице треба да го постави својот простор.\n\nСекој корисник може да ажурира апликации за сите други корисници. Поставките и услугите за пристапност не може да се префрлат на новиот корисник."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Кога додавате нов корисник, тоа лице треба да го постави својот простор.\n\nСекој корисник може да ажурира апликации за сите други корисници."</string> - <string name="user_grant_admin_title" msgid="5157031020083343984">"Дали да се направи корисников администратор?"</string> - <string name="user_grant_admin_message" msgid="1673791931033486709">"Администраторите имаат посебни привилегии што другите корисници ги немаат. Администраторот може да управува со сите корисници, да го ажурира или ресетира уредов, да ги менува поставките, да ги гледа сите инсталирани апликации и да доделува или одзема администраторски привилегии за други."</string> - <string name="user_grant_admin_button" msgid="5441486731331725756">"Направи да биде администратор"</string> + <string name="user_grant_admin_title" msgid="5157031020083343984">"Да се постави корисников како администратор?"</string> + <string name="user_grant_admin_message" msgid="1673791931033486709">"Администраторите имаат посебни привилегии што другите корисници ги немаат. Администраторот може да управува со сите корисници, да го ажурира или ресетира уредов, да ги менува поставките, да ги прегледува сите инсталирани апликации и да доделува или одзема администраторски привилегии на другите корисници."</string> + <string name="user_grant_admin_button" msgid="5441486731331725756">"Постави како администратор"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Ќе поставите корисник сега?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"Проверете дали лицето е достапно да го земе уредот и да го постави својот простор"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Постави профил сега?"</string> @@ -611,8 +614,8 @@ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Ова ќе започне нова гостинска сесија и ќе ги избрише сите апликации и податоци од тековната сесија"</string> <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Да се излезе од режим на гостин?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ова ќе ги избрише апликациите и податоците од тековната гостинска сесија"</string> - <string name="grant_admin" msgid="4323199171790522574">"Да, направи да биде администратор"</string> - <string name="not_grant_admin" msgid="3557849576157702485">"Не, не прави да биде администратори"</string> + <string name="grant_admin" msgid="4323199171790522574">"Да, постави како администратор"</string> + <string name="not_grant_admin" msgid="3557849576157702485">"Не, не поставувај како администратор"</string> <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Излези"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Да се зачува гостинската активност?"</string> <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Може да ја зачувате активноста од тековната сесија или да ги избришете сите апликации и податоци"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index f234392e650f..610f3653e716 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -545,12 +545,15 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ഇപ്പോൾ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ഈ ഫോൺ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ഈ ടാബ്ലെറ്റ്"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ഡോക്ക് സ്പീക്കർ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"കണക്റ്റ് ചെയ്ത ഉപകരണം"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ഈ ഉപകരണത്തിൽ പ്ലേ ചെയ്യാൻ കഴിയില്ല"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"അക്കൗണ്ട് മാറുന്നതിന്, അത് അപ്ഗ്രേഡ് ചെയ്യുക"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ഡൗൺലോഡ് ചെയ്തവ ഇവിടെ പ്ലേ ചെയ്യാനാകില്ല"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"അക്കൗണ്ട് മാറാൻ അപ്ഗ്രേഡ് ചെയ്യുക"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ഡൗൺലോഡുകൾ പ്ലേ ചെയ്യാനാകില്ല"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"പരസ്യത്തിന് ശേഷം വീണ്ടും ശ്രമിക്കുക"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ഇവിടെ പ്ലേ ചെയ്യാൻ ഉപകരണം സജീവമാക്കുക"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"പ്ലേ ചെയ്യാൻ ഉപകരണം സജീവമാക്കുക"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ഉപകരണത്തിന് പ്ലേ ചെയ്യാനുള്ള അനുമതിയില്ല"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ഈ മീഡിയ ഇവിടെ പ്ലേ ചെയ്യാൻ കഴിയില്ല"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്റ്റ് ചെയ്യുന്നതിൽ പ്രശ്നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 309b683a8e06..040f2c82c212 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Дөнгөж сая"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Энэ утас"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Энэ таблет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Суурилуулагчийн чанга яригч"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Холбогдсон төхөөрөмж"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Энэ утас"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Энэ төхөөрөмжид тоглуулах боломжгүй"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Сэлгэхийн тулд бүртгэлийг сайжруулна уу"</string> @@ -576,7 +579,7 @@ <string name="user_add_profile_item_title" msgid="3111051717414643029">"Хязгаарлагдсан профайл"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Шинэ хэрэглэгч нэмэх үү?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Та нэмэлт хэрэглэгч үүсгэх замаар бусад хүмүүстэй энэ төхөөрөмжийг хуваалцаж болно. Хэрэглэгч тус бүр апп, дэлгэцийн зураг болон бусад зүйлээ өөрчлөх боломжтой хувийн орон зайтай байна. Түүнчлэн хэрэглэгч нь бүх хэрэглэгчид нөлөөлөх боломжтой Wi-Fi зэрэг төхөөрөмжийн тохиргоог өөрчлөх боломжтой.\n\nХэрэв та шинэ хэрэглэгч нэмэх бол тухайн хүн хувийн орон зайгаа бүрдүүлэх ёстой.\n\nХэрэглэгч бүр бусад бүх хэрэглэгчийн өмнөөс апп шинэчилж болно. Хандалтын тохиргоо болон үйлчилгээг шинэ хэрэглэгчид шилжүүлэх боломжгүй байж болзошгүй."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"Та шинэ хэрэглэгч нэмбэл тухайн хүн өөрийн профайлыг тохируулах шаардлагатай.\n\nАль ч хэрэглэгч бусад бүх хэрэглэгчийн аппуудыг шинэчлэх боломжтой."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"Та шинэ хэрэглэгч нэмбэл тухайн хүн өөрийн орон зайг тохируулах шаардлагатай.\n\nАль ч хэрэглэгч бусад бүх хэрэглэгчийн аппуудыг шинэчлэх боломжтой."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Энэ хэрэглэгчийг админ болгох уу?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Админууд бусад хэрэглэгчид байхгүй тусгай эрхтэй байдаг. Админ нь бүх хэрэглэгчийг удирдах, энэ төхөөрөмжийг шинэчлэх, сэргээх, тохиргоог өөрчлөх, бүх суулгасан аппыг харах болон бусад хэрэглэгчид админы эрх өгөх эсвэл эрхийг нь цуцлах боломжтой."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Админ болгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 2a469b8144ab..932ef6c9e8dc 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"आत्ताच"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"हा फोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"हा टॅबलेट"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डॉक स्पीकर"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट केलेले डिव्हाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"या डिव्हाइसवर प्ले करू शकत नाही"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"स्विच करण्यासाठी खाते अपग्रेड करा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 31cd9f324f2c..460eb563adbc 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -545,9 +545,12 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Sebentar tadi"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Telefon ini"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tablet ini"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Pembesar suara dok"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Peranti Luar"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Peranti yang disambungkan"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefon ini"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat dimainkan pada peranti ini"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Tingkatkan akaun untuk bertukar"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Tingkatkan akaun untuk beralih"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tidak dapat memainkan muat turun di sini"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Cuba lagi selepas iklan"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Bangkitkan peranti untuk main di sini"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 18e89619702a..8458a629db79 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -545,12 +545,15 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ယခုလေးတင်"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ဤဖုန်း"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ဤတက်ဘလက်"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"အထိုင် စပီကာ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ချိတ်ဆက်ကိရိယာ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ဤစက်ပစ္စည်းတွင် ဖွင့်၍မရပါ"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"ပြောင်းရန်အကောင့်ကို အဆင့်မြှင့်ပါ"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ဤနေရာတွင် ဒေါင်းလုဒ်များကို ဖွင့်၍မရပါ"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"ပြောင်းရန် အကောင့်အဆင့်ကိုမြှင့်ပါ"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ဤနေရာတွင် ဒေါင်းလုဒ်များ ဖွင့်မရပါ"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"ကြော်ငြာအပြီးတွင် ထပ်စမ်းကြည့်ပါ"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ဤနေရာတွင်ဖွင့်ရန် စက်ပစ္စည်းကိုနှိုးပါ"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ဒီမှာဖွင့်ရန် စက်ပစ္စည်းကိုနှိုးပါ"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"စက်ပစ္စည်းက ဖွင့်ခွင့်မပြုပါ"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ဤမီဒီယာကို ဒီမှာဖွင့်၍မရပါ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index b505254fdb42..a082be68feac 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Nå nettopp"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Denne telefonen"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Dette nettbrettet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dokkhøyttaler"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhet"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tilkoblet enhet"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke spille på denne enheten"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Oppgrader kontoen for å bytte"</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Bruker"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Begrenset profil"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Vil du legge til en ny bruker?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enheten med andre folk ved å opprette flere brukere. Hver bruker har sin egen plass de kan tilpasse med apper, bakgrunner og annet. Brukere kan også justere enhetsinnstillinger, for eksempel wifi, som påvirker alle.\n\nNår du legger til en ny bruker, må vedkommende angi innstillinger for plassen sin.\n\nAlle brukere kan oppdatere apper for alle andre brukere. Innstillinger og tjenester for tilgjengelighet overføres kanskje ikke til den nye brukeren."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dele denne enheten med andre folk ved å opprette flere brukere. Hver bruker har sitt eget område de kan tilpasse med apper, bakgrunner og annet. Brukere kan også justere enhetsinnstillinger, for eksempel wifi, som påvirker alle.\n\nNår du legger til en ny bruker, må vedkommende angi innstillinger for sitt område.\n\nAlle brukere kan oppdatere apper for alle andre brukere. Innstillinger og tjenester for tilgjengelighet overføres kanskje ikke til den nye brukeren."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Når du legger til en ny bruker, må hen konfigurere sitt eget område.\n\nAlle brukere kan oppdatere apper for alle andre brukere."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Vil du gjøre denne brukeren til administrator?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Administratorer har spesielle rettigheter som ikke andre brukere har. Administratorer kan administrere alle brukere, oppdatere og tilbakestille denne enheten, endre innstillinger, se alle installerte apper, gi administratorrettigheter til andre samt oppheve andres administratorrettigheter."</string> diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index fccd8bbea1f2..f464f3135f49 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -23,7 +23,7 @@ <string-array name="wifi_status"> <item msgid="1596683495752107015"></item> <item msgid="3288373008277313483">"स्क्यान गरिँदै..."</item> - <item msgid="6050951078202663628">"जडान हुँदै..."</item> + <item msgid="6050951078202663628">"कनेक्ट गरिँदै छ..."</item> <item msgid="8356618438494652335">"प्रमाणित गर्दै ..."</item> <item msgid="2837871868181677206">"IP एड्रेस पत्ता लगाउँदै ..."</item> <item msgid="4613015005934755724">"जडान गरिएको"</item> @@ -37,7 +37,7 @@ <string-array name="wifi_status_with_ssid"> <item msgid="5969842512724979061"></item> <item msgid="1818677602615822316">"स्क्यान गर्दै..."</item> - <item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>सँग जडान हुँदै..."</item> + <item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>सँग कनेक्ट गरिँदै छ..."</item> <item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>को साथ प्रमाणित गर्दै…"</item> <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>बाट IP एड्रेस प्राप्त गर्दै…"</item> <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> मा कनेक्ट भएको छ छ"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index a7990fc6389a..4e3455256ba1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"विच्छेदन गरियो"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"जडान हटाइँदै ..."</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"जडान हुँदै..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"कनेक्ट गरिँदै छ..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> सँग कनेक्ट भएको छ"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"कनेक्ट गरिँदै छ..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"जडान गरियो (फोनबाहेेक) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -99,7 +99,7 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"ब्याट्रीको स्तर: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ब्याट्री, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ब्याट्री"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"सक्रिय"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"सक्रिय, बायाँ मात्र"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"बायाँ मात्र अन छ"</string> <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"सक्रिय, दायाँ मात्र"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"सक्रिय, बायाँ र दायाँ"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"मिडिया अडियो"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"अहिले भर्खरै"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"यो फोन"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"यो ट्याब्लेट"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"डक स्पिकर"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट गरिएको डिभाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"यो डिभाइसमा मिडिया प्ले गर्न मिल्दैन"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"आफूले प्रयोग गर्न चाहेको खाता अपग्रेड गर्नुहोस्"</string> @@ -592,7 +595,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"प्रोफाइलको जानकारी"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"निषेधयुक्त प्रोफाइल बनाउनु अघि तपाईँको एप र व्यक्तिगत डेटा सुरक्षा गर्नाका लागि तपाईँले स्क्रिन लक सेटअप गर्नु पर्दछ ।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"लक सेट गर्नुहोस्"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> पार्नुहोस्"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> बनाउनुहोस्"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नयाँ प्रयोगकर्ता बनाइँदै छ…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"नयाँ अतिथि बनाइँदै छ…"</string> <string name="add_user_failed" msgid="4809887794313944872">"नयाँ प्रयोगकर्ता सिर्जना गर्न सकिएन"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index ee02217d6f42..1b456b94f355 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Zojuist"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Deze telefoon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Deze tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockspeaker"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbonden apparaat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Deze telefoon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan niet afspelen op dit apparaat"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade het account om te schakelen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 5795abeeb599..99bcafef0543 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -84,9 +84,9 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"ବିଛିନ୍ନ ହେଲା"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"ବିଚ୍ଛିନ୍ନ କରୁଛି…"</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"ସଂଯୋଗ କରାଯାଉଛି…"</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"କନେକ୍ଟ ହେଉଛି…"</string> <string name="bluetooth_connected" msgid="8065345572198502293">"ସଂଯୁକ୍ତ ହେଲା<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"ପେୟାର୍ କରୁଛି…"</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"ପେୟାର କରୁଛି…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ସଂଯୁକ୍ତ ହେଲା (ମିଡିଆ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ କିମ୍ବା ମେଡିଆ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -174,7 +174,7 @@ <string name="launch_defaults_some" msgid="3631650616557252926">"କିଛି ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ ମାନ ସେଟ୍ ହୋଇଛି"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ଡିଫଲ୍ଟ ସେଟ୍ ହୋଇନାହିଁ"</string> <string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ସେଟିଂସ"</string> - <string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ୍-ଟୁ-ସ୍ପିଚ୍ ଆଉଟ୍ପୁଟ୍"</string> + <string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ-ଟୁ-ସ୍ପିଚ ଆଉଟପୁଟ"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପିଚ୍ ରେଟ୍"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"ଲେଖା ପଢ଼ିବାର ବେଗ"</string> <string name="tts_default_pitch_title" msgid="6988592215554485479">"ପିଚ୍"</string> @@ -250,7 +250,7 @@ <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"ଡିଭାଇସ୍ ପେୟାର୍ କରାଯାଉଛି…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"ଡିଭାଇସରୁ ପେୟାର୍ ହେବାରେ ବିଫଳ ହୋଇଛି। QR କୋଡ୍ ସଠିକ୍ ନଥିଲା ବା ଡିଭାଇସ୍ ସମାନ ନେଟୱାର୍କରେ ସଂଯୋଗ ହୋଇନାହିଁ।"</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP ଠିକଣା ଓ ପୋର୍ଟ"</string> - <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR କୋଡ୍ ସ୍କାନ୍ କରନ୍ତୁ"</string> + <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR କୋଡ ସ୍କାନ କରନ୍ତୁ"</string> <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"ଏକ QR କୋଡ୍ ସ୍କାନ୍ କରି ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଡିଭାଇସ୍ ପେୟାର୍ କରନ୍ତୁ"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"ଦୟାକରି ଏକ ୱାଇ-ଫାଇ ନେଟୱାର୍କରେ ସଂଯୋଗ କରନ୍ତୁ"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, ଡିବଗ୍, dev"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ଏହିକ୍ଷଣି"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ଏହି ଫୋନ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ଏହି ଟାବଲେଟ"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ଡକ ସ୍ପିକର"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"କନେକ୍ଟ କରାଯାଇଥିବା ଡିଭାଇସ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ଏହି ଡିଭାଇସରେ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ସ୍ୱିଚ କରିବା ପାଇଁ ଆକାଉଣ୍ଟକୁ ଅପଗ୍ରେଡ କରନ୍ତୁ"</string> @@ -578,7 +581,7 @@ <string name="user_add_user_message_long" msgid="1527434966294733380">"ଅତିରିକ୍ତ ୟୁଜରଙ୍କୁ ଯୋଗ କରି ଆପଣ ଏହି ଡିଭାଇସକୁ ଅନ୍ୟ ଲୋକମାନଙ୍କ ସହିତ ସେୟାର କରିପାରିବେ। ପ୍ରତ୍ୟେକ ୟୁଜରଙ୍କ ନିଜର ସ୍ପେସ ଅଛି ଯାହାକୁ ସେମାନେ ଆପ, ୱାଲପେପର ଓ ଏପରି ଅନେକ କିଛି ସହିତ କଷ୍ଟମାଇଜ କରିପାରିବେ। ୟୁଜର ୱାଇ-ଫାଇ ଭଳି ଡିଭାଇସ ସେଟିଂସକୁ ମଧ୍ୟ ଆଡଜଷ୍ଟ କରିପାରିବେ ଯାହା ସମସ୍ତଙ୍କୁ ପ୍ରଭାବିତ କରିଥାଏ। \n\nଯେତେବେଳେ ଆପଣ ଜଣେ ନୂଆ ୟୁଜରଙ୍କୁ ଯୋଗ କରିବେ, ସେତେବେଳେ ସେହି ବ୍ୟକ୍ତିଙ୍କୁ ନିଜର ସ୍ପେସକୁ ସେଟଅପ କରିବାକୁ ପଡ଼ିବ। \n\nଅନ୍ୟ ୟୁଜରଙ୍କ ପାଇଁ ଯେ କୌଣସି ୟୁଜର ଆପକୁ ଅପଡେଟ କରିପାରିବେ। ଆକ୍ସେସିବିଲିଟୀ ସେଟିଂସ ଏବଂ ସେବାଗୁଡ଼ିକ ନୂଆ ୟୁଜରଙ୍କୁ ସ୍ଥାନାନ୍ତର ହୋଇନପାରେ।"</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"ଆପଣ ଜଣେ ନୂଆ ୟୁଜରଙ୍କୁ ଯୋଗ କରିବା ବେଳେ, ସେହି ବ୍ୟକ୍ତିଙ୍କୁ ତାଙ୍କ ସ୍ପେସ ସେଟ ଅପ କରିବାକୁ ପଡ଼ିବ।\n\nଅନ୍ୟ ସମସ୍ତ ୟୁଜରଙ୍କ ପାଇଁ ଆପ୍ସକୁ ଯେ କୌଣସି ୟୁଜର ଅପଡେଟ କରିପାରିବେ।"</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"ଏହି ୟୁଜରଙ୍କୁ ଜଣେ ଆଡମିନ କରିବେ?"</string> - <string name="user_grant_admin_message" msgid="1673791931033486709">"ଆଡମିନମାନଙ୍କର ବିଶେଷ ଅଧିକାରଗୁଡ଼ିକ ଥାଏ ଯାହା ଅନ୍ୟ ୟୁଜରମାନଙ୍କର ନଥାଏ। ଜଣେ ଆଡମିନ ସମସ୍ତ ୟୁଜରଙ୍କୁ ପରିଚାଳନା କରିପାରିବେ, ଏହି ଡିଭାଇସକୁ ଅପଡେଟ କିମ୍ବା ରିସେଟ କରିପାରିବେ, ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିପାରିବେ, ଇନଷ୍ଟଲ କରାଯାଇଥିବା ସମସ୍ତ ଆପ୍ସ ଦେଖିପାରିବେ ଏବଂ ଅନ୍ୟମାନଙ୍କ ପାଇଁ ଆଡମିନ ବିଶେଷ ଅଧିକାରଗୁଡ଼ିକୁ ଅନୁମତି ଦେଇପାରିବେ କିମ୍ବା ପ୍ରତ୍ୟାହାର କରିପାରିବେ।"</string> + <string name="user_grant_admin_message" msgid="1673791931033486709">"ଆଡମିନମାନଙ୍କର ବିଶେଷ ଅଧିକାରଗୁଡ଼ିକ ଥାଏ ଯାହା ଅନ୍ୟ ୟୁଜରମାନଙ୍କର ନଥାଏ। ଜଣେ ଆଡମିନ ସମସ୍ତ ୟୁଜରଙ୍କୁ ପରିଚାଳନା କରିପାରିବେ, ଏହି ଡିଭାଇସକୁ ଅପଡେଟ କିମ୍ବା ରିସେଟ କରିପାରିବେ, ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିପାରିବେ, ଇନଷ୍ଟଲ କରାଯାଇଥିବା ସମସ୍ତ ଆପ୍ସ ଦେଖିପାରିବେ ଏବଂ ଅନ୍ୟମାନଙ୍କ ପାଇଁ ଆଡମିନଙ୍କ ବିଶେଷ ଅଧିକାରଗୁଡ଼ିକୁ ଅନୁମତି ଦେଇପାରିବେ କିମ୍ବା ପ୍ରତ୍ୟାହାର କରିପାରିବେ।"</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"ଆଡମିନ କରନ୍ତୁ"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"ଏବେ ଉପଯୋଗକର୍ତ୍ତା ସେଟଅପ କରିବେ?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ବ୍ୟକ୍ତି ଜଣକ ଡିଭାଇସ୍ ଓ ନିଜର ସ୍ଥାନ ସେଟଅପ୍ କରିବା ପାଇଁ ଉପଲବ୍ଧ ଅଛନ୍ତି।"</string> @@ -592,7 +595,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"ପ୍ରୋଫାଇଲ୍ ସୂଚନା"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"ପ୍ରତିବନ୍ଧିତ ପ୍ରୋଫାଇଲ୍ ତିଆରି କରିବାବେଳେ, ନିଜ ଆପ୍ ଓ ବ୍ୟକ୍ତିଗତ ତଥ୍ୟର ସୁରକ୍ଷା ପାଇଁ ଏକ ସ୍କ୍ରୀନ୍ ଲକ୍ ସେଟ୍ କରନ୍ତୁ।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ଲକ୍ ସେଟ୍ କରନ୍ତୁ"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସୁଇଚ କରନ୍ତୁ"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ନୂଆ ୟୁଜର ତିଆରି କରାଯାଉଛି…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ନୂଆ ଅତିଥି ତିଆରି କରାଯାଉଛି…"</string> <string name="add_user_failed" msgid="4809887794313944872">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରିବାକୁ ବିଫଳ ହେଲା"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 7472e5da89dd..3e202010c85f 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ਹੁਣੇ ਹੀ"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ਇਹ ਫ਼ੋਨ"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ਇਹ ਟੈਬਲੈੱਟ"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ਡੌਕ ਸਪੀਕਰ"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ਸਵਿੱਚ ਕਰਨ ਲਈ ਖਾਤੇ ਨੂੰ ਅੱਪਗ੍ਰੇਡ ਕਰੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/arrays.xml b/packages/SettingsLib/res/values-pl/arrays.xml index f665850af430..031cd9c04910 100644 --- a/packages/SettingsLib/res/values-pl/arrays.xml +++ b/packages/SettingsLib/res/values-pl/arrays.xml @@ -257,7 +257,7 @@ <item msgid="1212561935004167943">"Wyróżnij testowane polecenia rysowania na zielono"</item> </string-array> <string-array name="track_frame_time_entries"> - <item msgid="634406443901014984">"Wyłączone"</item> + <item msgid="634406443901014984">"Wyłączono"</item> <item msgid="1288760936356000927">"Na ekranie w postaci pasków"</item> <item msgid="5023908510820531131">"W: <xliff:g id="AS_TYPED_COMMAND">adb shell dumpsys gfxinfo</xliff:g>"</item> </string-array> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 8821bdc52b0e..58a89d96e5ac 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -84,7 +84,7 @@ <string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string> <string name="bluetooth_disconnected" msgid="7739366554710388701">"Rozłączono"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Rozłączanie..."</string> - <string name="bluetooth_connecting" msgid="5871702668260192755">"Łączenie..."</string> + <string name="bluetooth_connecting" msgid="5871702668260192755">"Łączę..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Połączono – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Paruję…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Połączono (bez telefonu) – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -99,8 +99,8 @@ <string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii"</string> <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P: bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string> <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Urządzenie aktywne"</string> - <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktywny, tylko lewa strona"</string> - <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktywny, tylko prawa strona"</string> + <string name="bluetooth_hearing_aid_left_active" msgid="7084887715570971441">"Aktywne, tylko lewa strona"</string> + <string name="bluetooth_hearing_aid_right_active" msgid="8574683234077567230">"Aktywne, tylko prawa strona"</string> <string name="bluetooth_hearing_aid_left_and_right_active" msgid="407704460573163973">"Aktywny, lewa i prawa strona"</string> <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"Dźwięk multimediów"</string> <string name="bluetooth_profile_headset" msgid="5395952236133499331">"Połączenia telefoniczne"</string> @@ -545,12 +545,15 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Przed chwilą"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ten telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ten tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Głośnik ze stacją dokującą"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Urządzenie zewnętrzne"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Połączone urządzenie"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nie można odtworzyć na tym urządzeniu"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uaktualnij konto, aby przełączyć"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Aby przełączyć, potrzebujesz konta premium"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tutaj nie można odtworzyć pobranych plików"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Spróbuj ponownie po reklamie"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wybudź urządzenie, aby odtworzyć tutaj"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Wybudź, aby tu odtworzyć"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Urządzenie nie ma uprawnień do odtwarzania"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Nie można odtworzyć tego pliku multimedialnego"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Użytkownik"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Profil ograniczony"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Dodać nowego użytkownika?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Z tego urządzenia możesz korzystać wraz z innymi osobami, dodając na nim konta użytkowników. Każdy użytkownik ma własne miejsce na swoje aplikacje, tapety i inne dane. Może też zmieniać ustawienia, które wpływają na wszystkich użytkowników urządzenia (np. Wi‑Fi).\n\nGdy dodasz nowego użytkownika, musi on skonfigurować swoje miejsce na dane.\n\nKażdy użytkownik może aktualizować aplikacje w imieniu wszystkich pozostałych użytkowników. Ułatwienia dostępu i usługi mogą nie zostać przeniesione na konto nowego użytkownika."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Na tym urządzeniu możesz dodać konta użytkowników i dzięki temu korzystać z niego wraz z innymi osobami. Każdy użytkownik ma własne miejsce na swoje aplikacje, tapety i inne dane. Może też zmieniać ustawienia (np. Wi‑Fi), które wpływają na wszystkich użytkowników urządzenia.\n\nGdy dodasz nowego użytkownika, musi on skonfigurować swoje miejsce na dane.\n\nKażdy użytkownik może aktualizować aplikacje wszystkich pozostałych użytkowników. Ułatwienia dostępu i usługi mogą nie zostać przeniesione na konto nowego użytkownika."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Gdy dodasz nowego użytkownika, musi on skonfigurować swoją przestrzeń.\n\nKażdy użytkownik może aktualizować aplikacje wszystkich innych użytkowników."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Przyznać temu użytkownikowi uprawnienia administratora?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Administratorzy mają specjalne uprawnienia, którymi nie dysponują pozostali użytkownicy. Administrator może zarządzać wszystkimi użytkownikami, aktualizować i resetować urządzenie, modyfikować ustawienia, wyświetlać wszystkie zainstalowane aplikacje oraz przyznawać uprawnienia administratora innym użytkownikom i je wycofywać."</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 15331c3bb13c..678c076aa03b 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telefone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telefone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string> @@ -575,9 +578,9 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Usuário"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Perfil restrito"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Adicionar novo usuário?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Você pode compartilhar este dispositivo com outras pessoas, adicionando usuários. Cada usuário tem o próprio espaço, que pode ser personalizado com apps, planos de fundo, etc. Os usuários também podem ajustar as configurações do dispositivo, como o Wi‑Fi, que afetam a todos.\n\nQuando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para todos os outros. Serviços e configurações de acessibilidade podem não ser transferidos para novos usuários."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Você pode compartilhar este dispositivo com outras pessoas, adicionando usuários. Cada um tem o próprio espaço de trabalho, que pode ser personalizado com apps, planos de fundo, etc. Também é possível ajustar as configurações do dispositivo, como o Wi‑Fi, que afetam a todos.\n\nQuando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para todos os outros. Serviços e configurações de acessibilidade podem não ser transferidos para novos usuários."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Quando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para os demais usuários."</string> - <string name="user_grant_admin_title" msgid="5157031020083343984">"Transformar esse usuário um administrador?"</string> + <string name="user_grant_admin_title" msgid="5157031020083343984">"Tornar esse usuário um administrador?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Administradores têm privilégios especiais que outros usuários não têm. Um administrador pode gerenciar todos os usuários, atualizar ou redefinir este dispositivo, modificar configurações, conferir todos os apps instalados e conceder ou revogar privilégios de administrador para outras pessoas."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Transformar o usuário em administrador"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurar o usuário agora?"</string> @@ -611,7 +614,7 @@ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Essa ação vai iniciar uma nova Sessão de visitante e excluir todos os apps e dados da sessão atual"</string> <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo visitante?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Essa ação vai excluir apps e dados da Sessão de visitante atual"</string> - <string name="grant_admin" msgid="4323199171790522574">"Sim, transformar esse usuário em administrador"</string> + <string name="grant_admin" msgid="4323199171790522574">"Sim, tornar esse usuário um administrador"</string> <string name="not_grant_admin" msgid="3557849576157702485">"Não, não transformar o usuário em administrador"</string> <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvar a atividade do visitante?"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 08a4422bd338..847bfb5634e7 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telemóvel"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altifalante estação carregam."</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Atualize a conta para mudar"</string> @@ -576,7 +579,7 @@ <string name="user_add_profile_item_title" msgid="3111051717414643029">"Perfil restrito"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Adicionar novo utilizador?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Pode partilhar este dispositivo com outras pessoas ao criar utilizadores adicionais. Cada utilizador possui o seu próprio espaço, que pode ser personalizado com apps, imagens de fundo, etc. Os utilizadores também podem ajustar as definições do dispositivo, como o Wi‑Fi, que afetam os restantes utilizadores.\n\nAo adicionar um novo utilizador, essa pessoa tem de configurar o respetivo espaço.\n\nQualquer utilizador pode atualizar apps para todos os outros utilizadores. Os serviços e as definições de acessibilidade podem não ser transferidos para o novo utilizador."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"Ao adicionar um novo utilizador, essa pessoa tem de configurar o respetivo espaço.\n\nQualquer utilizador pode atualizar aplicações para todos os outros utilizadores."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"Ao adicionar um novo utilizador, essa pessoa tem de configurar o respetivo espaço.\n\nQualquer utilizador pode atualizar apps para todos os outros utilizadores."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Definir este utilizador como um administrador?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Os administradores têm privilégios especiais que outros utilizadores não têm. Um administrador pode gerir todos os utilizadores, atualizar ou repor este dispositivo, modificar definições, ver todas as apps instaladas e revogar ou conceder privilégios de administrador a outras pessoas."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Definir como administrador"</string> @@ -588,7 +591,7 @@ <string name="user_add_user_type_title" msgid="551279664052914497">"Adicionar"</string> <string name="user_new_user_name" msgid="60979820612818840">"Novo utilizador"</string> <string name="user_new_profile_name" msgid="2405500423304678841">"Novo perfil"</string> - <string name="user_info_settings_title" msgid="6351390762733279907">"Info. utilizador"</string> + <string name="user_info_settings_title" msgid="6351390762733279907">"Dados do utilizador"</string> <string name="profile_info_settings_title" msgid="105699672534365099">"Informação do perfil"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de poder criar um perfil restrito, tem de configurar um bloqueio de ecrã para proteger as suas aplicações e dados pessoais."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 15331c3bb13c..678c076aa03b 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Agora"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Este telefone"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Este tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Alto-falante da base"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telefone"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string> @@ -575,9 +578,9 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Usuário"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Perfil restrito"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Adicionar novo usuário?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Você pode compartilhar este dispositivo com outras pessoas, adicionando usuários. Cada usuário tem o próprio espaço, que pode ser personalizado com apps, planos de fundo, etc. Os usuários também podem ajustar as configurações do dispositivo, como o Wi‑Fi, que afetam a todos.\n\nQuando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para todos os outros. Serviços e configurações de acessibilidade podem não ser transferidos para novos usuários."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Você pode compartilhar este dispositivo com outras pessoas, adicionando usuários. Cada um tem o próprio espaço de trabalho, que pode ser personalizado com apps, planos de fundo, etc. Também é possível ajustar as configurações do dispositivo, como o Wi‑Fi, que afetam a todos.\n\nQuando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para todos os outros. Serviços e configurações de acessibilidade podem não ser transferidos para novos usuários."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Quando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para os demais usuários."</string> - <string name="user_grant_admin_title" msgid="5157031020083343984">"Transformar esse usuário um administrador?"</string> + <string name="user_grant_admin_title" msgid="5157031020083343984">"Tornar esse usuário um administrador?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Administradores têm privilégios especiais que outros usuários não têm. Um administrador pode gerenciar todos os usuários, atualizar ou redefinir este dispositivo, modificar configurações, conferir todos os apps instalados e conceder ou revogar privilégios de administrador para outras pessoas."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Transformar o usuário em administrador"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurar o usuário agora?"</string> @@ -611,7 +614,7 @@ <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Essa ação vai iniciar uma nova Sessão de visitante e excluir todos os apps e dados da sessão atual"</string> <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo visitante?"</string> <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Essa ação vai excluir apps e dados da Sessão de visitante atual"</string> - <string name="grant_admin" msgid="4323199171790522574">"Sim, transformar esse usuário em administrador"</string> + <string name="grant_admin" msgid="4323199171790522574">"Sim, tornar esse usuário um administrador"</string> <string name="not_grant_admin" msgid="3557849576157702485">"Não, não transformar o usuário em administrador"</string> <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string> <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvar a atividade do visitante?"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index edad3bd9ed5f..7fa228bc5be6 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Chiar acum"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Acest telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Această tabletă"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Difuzorul dispozitivului de andocare"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispozitiv extern"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispozitiv conectat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nu se poate reda pe acest dispozitiv"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Fă upgrade contului pentru a comuta"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 6b5a4a0ff838..a53b67afc002 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -86,7 +86,7 @@ <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Отключение..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Подключение..."</string> <string name="bluetooth_connected" msgid="8065345572198502293">"Подключено<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_pairing" msgid="4269046942588193600">"Устанавливается соединение..."</string> + <string name="bluetooth_pairing" msgid="4269046942588193600">"Подключение..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Подключено (кроме звонков)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Подключено (кроме аудио)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Подключено (кроме звонков и аудио)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Только что"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Этот смартфон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Этот планшет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Колонка с док-станцией"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Подключенное устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Невозможно воспроизвести на этом устройстве."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Для переключения требуется премиум-аккаунт"</string> @@ -578,7 +581,7 @@ <string name="user_add_user_message_long" msgid="1527434966294733380">"Если этим устройством пользуются сразу несколько человек, для каждого из них можно создать профиль – отдельное пространство с выбранными приложениями, обоями и т. д. Новый пользователь настраивает его сам.\n\nИз профиля можно поменять и общие настройки устройства, например сеть Wi-Fi.\n\nОбновлять общие приложения могут все, однако специальные возможности настраиваются индивидуально."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"После создания профиля его потребуется настроить.\n\nЛюбой пользователь устройства может обновлять приложения для всех аккаунтов."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Назначить этого пользователя администратором?"</string> - <string name="user_grant_admin_message" msgid="1673791931033486709">"У администраторов права шире, чем у других пользователей. Администратор может управлять всеми пользователями, обновлять и сбрасывать это устройство, менять настройки, просматривать установленные приложения, а также предоставлять и отзывать права администратора."</string> + <string name="user_grant_admin_message" msgid="1673791931033486709">"У администраторов права шире, чем у других пользователей. Администратор может управлять аккаунтами всех пользователей, обновлять ПО на устройстве, менять и сбрасывать его настройки, просматривать установленные приложения, а также предоставлять и отзывать права администратора."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Назначить администратором"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"Настроить профиль?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"Вам потребуется передать устройство пользователю, чтобы он мог настроить свой профиль."</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 1dbe5a02fe90..ab5d3ceb8705 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"මේ දැන්"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"මෙම දුරකථනය"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"මෙම ටැබ්ලටය"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ඩොක් ස්පීකරය"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"සම්බන්ධ කළ උපාංගය"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"මෙම දුරකථනය"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"මෙම උපාංගය මත ධාවනය කළ නොහැක"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"මාරු වීමට ගිණුම උත්ශ්රේණි කරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index b29d0edcc5ad..daf9492b351d 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -107,7 +107,7 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľanie kontaktov a histórie hovorov"</string> + <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Zdieľať kontakty a históriu hovorov"</string> <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Používané pri zdieľaní kontaktov a histórie hovorov"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Teraz"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Tento telefón"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Tento tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Reproduktor doku"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externé zariadenie"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pripojené zariadenie"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V tomto zariadení sa nedá prehrávať obsah"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Inovujte účet a prejdite naň"</string> @@ -552,7 +555,7 @@ <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Skúste to znova po reklame"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ak chcete prehrávať obsah tu, prebuďte zariadenie"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Prehrávanie v tomto zariadení nie je schválené"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Tu sa toto médium nedá prehrať"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Médium sa tu nedá prehrať"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string> <string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string> @@ -575,8 +578,8 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Používateľ"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Obmedzený profil"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Pridať nového používateľa?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Vytvorením ďalších používateľov môžete toto zariadenie zdieľať s inými ľuďmi. Každý používateľ má svoje prostredie, ktoré si môže prispôsobiť vlastnými aplikáciami, tapetou atď. Používatelia tiež môžu upraviť nastavenia zariadenia (napr. Wi-Fi), ktoré ovplyvnia všetkých používateľov.\n\nKeď pridáte nového používateľa, musí si nastaviť vlastný priestor.\n\nAkýkoľvek používateľ môže aktualizovať aplikácie všetkých používateľov. Nastavenia dostupnosti a služby sa nemusia preniesť novému používateľovi."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"Keď pridáte nového používateľa, musí si nastaviť vlastný priestor.\n\nAkýkoľvek používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Vytvorením ďalších používateľov môžete toto zariadenie zdieľať s inými ľuďmi. Každý používateľ má svoje prostredie, ktoré si môže prispôsobiť vlastnými aplikáciami, tapetou atď. Používatelia tiež môžu upraviť nastavenia zariadenia (napr. Wi-Fi), ktoré ovplyvnia všetkých používateľov.\n\nKeď pridáte nového používateľa, musí si nastaviť vlastný priestor.\n\nKaždý používateľ môže aktualizovať aplikácie všetkých používateľov. Nastavenia dostupnosti a služby sa nemusia preniesť novému používateľovi."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"Keď pridáte nového používateľa, musí si nastaviť vlastný priestor.\n\nKaždý používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Chcete tohto používateľa nastaviť ako správcu?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Správcovia majú oproti iným používateľom špeciálne oprávnenia. Správca môže ovládať všetkých používateľov, aktualizovať alebo resetovať toto zariadenie, upravovať nastavenia, prezerať všetky nainštalované aplikácie a udeľovať či odoberať správcovské oprávnenia iným používateľom."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Nastaviť ako správcu"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 6b3f63e2014a..7164da61c5d5 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Pravkar"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ta telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ta tablični računalnik"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Zvočnik nosilca"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Zunanja naprava"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezana naprava"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ni mogoče predvajati v tej napravi."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Za preklop je potrebna nadgradnja računa"</string> @@ -592,7 +595,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"Podatki za profil"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"Preden lahko ustvarite profil z omejitvami, morate nastaviti zaklepanje zaslona, da zaščitite aplikacije in osebne podatke."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavi zaklepanje"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"Preklopi na račun <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"Preklopi na uporabnika <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"Ustvarjanje novega gosta …"</string> <string name="add_user_failed" msgid="4809887794313944872">"Ustvarjanje novega uporabnika ni uspelo."</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 7d43c0a1828e..7fd6ec9ab15f 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -455,7 +455,7 @@ <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> bazuar në përdorimin tënd"</string> <string name="power_discharge_by" msgid="4113180890060388350">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"Duhet të zgjasë deri në rreth <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Deri në <xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_discharge_by_only_short" msgid="5883041507426914446">"Deri në: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria mund të mbarojë deri në <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> e mbetur"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> e mbetur (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -520,7 +520,7 @@ <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Më shumë kohë."</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Më pak kohë."</string> <string name="cancel" msgid="5665114069455378395">"Anulo"</string> - <string name="next" msgid="2699398661093607009">"Tjetri"</string> + <string name="next" msgid="2699398661093607009">"Para"</string> <string name="back" msgid="5554327870352703710">"Prapa"</string> <string name="save" msgid="3745809743277153149">"Ruaj"</string> <string name="okay" msgid="949938843324579502">"Në rregull"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Pikërisht tani"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ky telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ky tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Altoparlanti i stacionit"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nuk mund të luhet në këtë pajisje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Përmirëso llogarinë për të ndryshuar"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 34716461b55d..86675cf6f9ba 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Управо"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Овај телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Овај таблет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Звучник базне станице"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Повезани уређај"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овај телефон"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можете да пустите на овом уређају"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надоградите налог ради пребацивања"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index c264192dc1ec..0d71afd98fa9 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Nyss"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Den här telefonen"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Den här surfplattan"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dockningsstationens högtalare"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern enhet"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ansluten enhet"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan inte spelas på denna enhet"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppgradera kontot för att byta"</string> @@ -576,7 +579,7 @@ <string name="user_add_profile_item_title" msgid="3111051717414643029">"Begränsad profil"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Lägga till ny användare?"</string> <string name="user_add_user_message_long" msgid="1527434966294733380">"Du kan dela enheten med andra om du skapar flera användare. Alla användare får sitt eget utrymme som de kan anpassa som de vill med appar, bakgrund och så vidare. Användarna kan även ändra enhetsinställningar som påverkar alla, till exempel wifi.\n\nNär du lägger till en ny användare måste han eller hon konfigurera sitt utrymme.\n\nAlla användare kan uppdatera appar för samtliga användares räkning. Tillgänglighetsinställningar och tjänster kanske inte överförs till den nya användaren."</string> - <string name="user_add_user_message_short" msgid="3295959985795716166">"När du lägger till en ny användare måste den personen konfigurera sitt utrymme.\n\nAlla användare kan uppdatera appar för samtliga användares räkning."</string> + <string name="user_add_user_message_short" msgid="3295959985795716166">"När du lägger till en ny användare måste den personen konfigurera sitt utrymme.\n\nAlla användare kan uppdatera appar för samtliga användare."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Vill du göra denna användare till administratör?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Administratörer har särskilda behörigheter som andra användare inte har. En administratör kan hantera alla användare, uppdatera eller återställa den här enheten, ändra inställningar, se alla installerade appar och bevilja eller återkalla administratörsbehörigheter för andra."</string> <string name="user_grant_admin_button" msgid="5441486731331725756">"Gör till administratör"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index e104582a1879..303131e3a5f2 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -214,7 +214,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"Chagua wasifu"</string> <string name="category_personal" msgid="6236798763159385225">"Binafsi"</string> - <string name="category_work" msgid="4014193632325996115">"Ya Kazini"</string> + <string name="category_work" msgid="4014193632325996115">"Kazini"</string> <string name="category_clone" msgid="1554511758987195974">"Kloni"</string> <string name="development_settings_title" msgid="140296922921597393">"Chaguo za wasanidi"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Washa chaguo za wasanidi programu"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Sasa hivi"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Simu hii"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Kompyuta kibao hii"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Spika ya kituo"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kifaa cha Nje"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Kifaa kilichounganishwa"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Simu hii"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Huwezi kucheza maudhui kwenye kifaa hiki"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Pata toleo jipya la akaunti ili ubadilishe"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 33cde5b6cc94..83aa2aff8a1c 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"சற்றுமுன்"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"இந்த மொபைல்"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"இந்த டேப்லெட்"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"டாக் ஸ்பீக்கர்"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"இணைக்கப்பட்டுள்ள சாதனம்"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"இந்தச் சாதனத்தில் பிளே செய்ய முடியவில்லை"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"மாற்ற, கணக்கை மேம்படுத்துங்கள்"</string> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index db2307d9b618..d250352ea811 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -66,7 +66,7 @@ <string-array name="bt_hci_snoop_log_filters_entries"> <item msgid="195768089203590086">"ACLహెడర్లను మాత్రమే వదిలివేయండి"</item> <item msgid="2776218217644557831">"A2DP మీడియా ప్యాకెట్లను ఫిల్టర్ చేయండి"</item> - <item msgid="8163235976612675092">"RFCOMM ఛానెల్ని ఫిల్టర్ చేయండి"</item> + <item msgid="8163235976612675092">"RFCOMM ఛానెల్ను ఫిల్టర్ చేయండి"</item> </string-array> <string-array name="bt_hci_snoop_log_profile_filter_entries"> <item msgid="3961868665260627524">"డిజేబుల్ చేయండి"</item> @@ -269,10 +269,10 @@ <string-array name="app_process_limit_entries"> <item msgid="794656271086646068">"స్టాండర్డ్ పరిమితి"</item> <item msgid="8628438298170567201">"నేపథ్య ప్రాసెస్లు లేవు"</item> - <item msgid="915752993383950932">"గరిష్టంగా 1 ప్రాసెస్"</item> - <item msgid="8554877790859095133">"గరిష్టంగా 2 ప్రాసెస్లు"</item> - <item msgid="9060830517215174315">"గరిష్టంగా 3 ప్రాసెస్లు"</item> - <item msgid="6506681373060736204">"గరిష్టంగా 4 ప్రాసెస్లు"</item> + <item msgid="915752993383950932">"గరిష్ఠంగా 1 ప్రాసెస్"</item> + <item msgid="8554877790859095133">"గరిష్ఠంగా 2 ప్రాసెస్లు"</item> + <item msgid="9060830517215174315">"గరిష్ఠంగా 3 ప్రాసెస్లు"</item> + <item msgid="6506681373060736204">"గరిష్ఠంగా 4 ప్రాసెస్లు"</item> </string-array> <string-array name="usb_configuration_titles"> <item msgid="3358668781763928157">"ఛార్జింగ్"</item> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index fcdad545c598..e7cf933b05ad 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -214,7 +214,7 @@ </string-array> <string name="choose_profile" msgid="343803890897657450">"ప్రొఫైల్ను ఎంచుకోండి"</string> <string name="category_personal" msgid="6236798763159385225">"వ్యక్తిగతం"</string> - <string name="category_work" msgid="4014193632325996115">"ఆఫీస్"</string> + <string name="category_work" msgid="4014193632325996115">"వర్క్"</string> <string name="category_clone" msgid="1554511758987195974">"క్లోన్ చేయండి"</string> <string name="development_settings_title" msgid="140296922921597393">"డెవలపర్ ఆప్షన్లు"</string> <string name="development_settings_enable" msgid="4285094651288242183">"డెవలపర్ ఎంపికలను ప్రారంభించండి"</string> @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ఇప్పుడే"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"ఈ ఫోన్"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"ఈ టాబ్లెట్"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"డాక్ స్పీకర్"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్టర్నల్ పరికరం"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"కనెక్ట్ చేసిన పరికరం"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ఈ పరికరంలో ప్లే చేయడం సాధ్యపడదు"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"మారడానికి ఖాతాను అప్గ్రేడ్ చేయండి"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ఇక్కడ డౌన్లోడ్లను ప్లే చేయడం సాధ్యపడదు"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"యాడ్ తర్వాత మళ్లీ ట్రై చేయండి"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"ఇక్కడ ప్లే చేయడానికి పరికరాన్ని మేల్కొలపండి"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ప్లే చేయడానికి పరికరానికి అధికారం ఇవ్వలేదు"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ఇక్కడ ఈ మీడియాను ప్లే చేయడం సాధ్యపడదు"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"ప్లే చేయడానికి పరికరానికి అనుమతి లేదు"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"ఈ మీడియాను ఇక్కడ ప్లే చేయడం సాధ్యపడదు."</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string> <string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string> @@ -575,11 +578,11 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"యూజర్"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"పరిమితం చేయబడిన ప్రొఫైల్"</string> <string name="user_add_user_title" msgid="5457079143694924885">"కొత్త యూజర్ను జోడించాలా?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"అదనపు యూజర్లను క్రియేట్ చేయడం ద్వారా మీరు ఈ పరికరాన్ని ఇతరులతో షేర్ చేయవచ్చు. ప్రతి యూజర్కు వారికంటూ ప్రత్యేక స్థలం ఉంటుంది, వారు ఆ స్థలాన్ని యాప్లు, వాల్పేపర్ మొదలైనవాటితో అనుకూలంగా మార్చవచ్చు. యూజర్లు ప్రతి ఒక్కరిపై ప్రభావం చూపే Wi‑Fi వంటి పరికర సెట్టింగ్లను కూడా సర్దుబాటు చేయవచ్చు.\n\nమీరు కొత్త యూజర్ను జోడించినప్పుడు, ఆ వ్యక్తి వారికంటూ స్వంత స్థలం సెట్ చేసుకోవాలి.\n\nఏ యూజర్ అయినా మిగిలిన యూజర్లందరి కోసం యాప్లను అప్డేట్ చేయవచ్చు. యాక్సెసిబిలిటీ సెట్టింగ్లు, సర్వీస్లు కొత్త యూజర్కి బదిలీ కాకపోవచ్చు."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"అదనపు యూజర్లను క్రియేట్ చేయడం ద్వారా మీరు ఈ పరికరాన్ని ఇతరులతో షేర్ చేయవచ్చు. ప్రతి యూజర్కు వారికంటూ ప్రత్యేక స్పేస్ ఉంటుంది, వారు ఆ స్పేస్ను యాప్లు, వాల్పేపర్ మొదలైనవాటితో అనుకూలంగా మార్చుకోవచ్చు. యూజర్లు ప్రతి ఒక్కరిపై ప్రభావం చూపే Wi‑Fi వంటి పరికర సెట్టింగ్లను కూడా సర్దుబాటు చేయవచ్చు.\n\nమీరు కొత్త యూజర్ను జోడించినప్పుడు, ఆ వ్యక్తి వారికంటూ స్వంత స్పేస్ను సెట్ చేసుకోవాలి.\n\nఏ యూజర్ అయినా మిగిలిన యూజర్లందరి కోసం యాప్లను అప్డేట్ చేయవచ్చు. యాక్సెసిబిలిటీ సెట్టింగ్లు, సర్వీస్లు కొత్త యూజర్కి బదిలీ కాకపోవచ్చు."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"మీరు కొత్త యూజర్ను జోడించినప్పుడు, ఆ వ్యక్తి తన స్పేస్ను సెటప్ చేసుకోవాలి.\n\nఏ యూజర్ అయినా మిగతా యూజర్ల కోసం యాప్లను అప్డేట్ చేయగలరు."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"ఈ యూజర్ను అడ్మిన్ చేయాలా?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"యూజర్లకు లేని ప్రత్యేక హక్కులు అడ్మిన్లకు ఉంటాయి. అడ్మిన్, యూజర్లందరినీ మేనేజ్ చేయగలరు, ఈ పరికరాన్ని అప్డేట్ లేదా రీసెట్ చేయగలరు, సెట్టింగ్లను మార్చగలరు, ఇన్స్టాల్ అయ్యి ఉండే యాప్లన్నింటినీ చూడగలరు, ఇతరులకు అడ్మిన్ హక్కులను ఇవ్వగలరు, లేదా ఉపసంహరించగలరు."</string> - <string name="user_grant_admin_button" msgid="5441486731331725756">"అడ్మిన్గా చేయాలి"</string> + <string name="user_grant_admin_button" msgid="5441486731331725756">"అడ్మిన్గా చేయండి"</string> <string name="user_setup_dialog_title" msgid="8037342066381939995">"యూజర్ను ఇప్పుడే సెటప్ చేయాలా?"</string> <string name="user_setup_dialog_message" msgid="269931619868102841">"పరికరాన్ని తీసుకోవడానికి వ్యక్తి అందుబాటులో ఉన్నారని నిర్ధారించుకొని, ఆపై వారికి స్టోరేజ్ స్థలాన్ని సెటప్ చేయండి"</string> <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"ఇప్పుడు ప్రొఫైల్ను సెటప్ చేయాలా?"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 6595c6b5e3f1..32629c32f969 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"เมื่อสักครู่"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"โทรศัพท์เครื่องนี้"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"แท็บเล็ตเครื่องนี้"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"แท่นวางลำโพง"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"อุปกรณ์ที่เชื่อมต่อ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"โทรศัพท์เครื่องนี้"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"เล่นในอุปกรณ์นี้ไม่ได้"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"อัปเกรดบัญชีเพื่อเปลี่ยน"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index b6616aa5c98e..e60faf6bc823 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Ngayon lang"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Ang teleponong ito"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Ang tablet na ito"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Speaker ng dock"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External na Device"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Nakakonektang device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ang teleponong ito"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Hindi ma-play sa device na ito"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"I-upgrade ang account para lumipat"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 2d9ca2b5d081..3ea12adfd3df 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -545,10 +545,13 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Az önce"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Bu telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Bu tablet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Yuva hoparlörü"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Harici Cihaz"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Bağlı cihaz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oynatılamıyor"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Geçiş yapmak için hesabı yükseltin"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"İndirilenler burada oynatılamıyor"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"İndirilenler burada oynatılamaz"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Reklamdan sonra tekrar deneyin"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Burada oynatmak için cihazı uyandırın"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Cihaz, oynatma işlemini onaylamadı"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 9a0c136da36d..95ece2660b76 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -107,7 +107,7 @@ <string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string> <string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string> <string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string> - <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ до контактів та історії дзвінків"</string> + <string name="bluetooth_profile_pbap" msgid="4262303387989406171">"Доступ до контактів і історії викликів"</string> <string name="bluetooth_profile_pbap_summary" msgid="6466456791354759132">"Використовуйте, щоб надсилати контакти й історію викликів"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Надання доступу до Інтернету"</string> <string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстові повідомлення"</string> @@ -545,14 +545,17 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Щойно"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Цей телефон"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Цей планшет"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Динамік док-станції"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Підключений пристрій"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string> - <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можна відтворювати медіаконтент на цьому пристрої"</string> - <string name="media_output_status_require_premium" msgid="8411255800047014822">"Щоб змінити, перейдіть на платний обліковий запис"</string> - <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Тут не можна відтворювати завантажений контент"</string> + <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можна відтворювати тут"</string> + <string name="media_output_status_require_premium" msgid="8411255800047014822">"Потрібний платний обліковий запис"</string> + <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Завантаження не відтворюватимуться"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Повторіть спробу після реклами"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Виведіть пристрій із режиму сну, щоб відтворювати медіаконтент"</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Пристрій не схвалений для відтворення медіаконтенту"</string> - <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"На пристрої не можна відтворювати цей медіафайл"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Виведіть із режиму сну, щоб відтворювати"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Не схвалено для відтворення"</string> + <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Цей медіаконтент не підтримується"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string> <string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string> @@ -575,7 +578,7 @@ <string name="user_add_user_item_title" msgid="2394272381086965029">"Користувач"</string> <string name="user_add_profile_item_title" msgid="3111051717414643029">"Профіль з обмеженням"</string> <string name="user_add_user_title" msgid="5457079143694924885">"Додати нового користувача?"</string> - <string name="user_add_user_message_long" msgid="1527434966294733380">"Цим пристроєм можуть користуватися кілька людей. Для цього потрібно створити додаткові профілі. Власник профілю може налаштувати його на свій смак: вибрати фоновий малюнок, установити потрібні додатки тощо. Користувачі також можуть налаштовувати певні параметри пристрою (як-от Wi-Fi), які застосовуватимуться до решти профілів.\n\nПісля створення новий профіль потрібно налаштувати.\n\nБудь-який користувач пристрою може оновлювати додатки для решти користувачів. Налаштування спеціальних можливостей і сервісів можуть не передаватися новому користувачеві."</string> + <string name="user_add_user_message_long" msgid="1527434966294733380">"Цим пристроєм можуть користуватися кілька людей. Для цього потрібно створити додаткові профілі. Власник профілю може налаштувати його на свій смак: вибрати фоновий малюнок, установити потрібні додатки тощо. Користувачі також можуть налаштовувати певні параметри пристрою (як-от Wi-Fi), які застосовуватимуться до решти профілів.\n\nПісля створення новий профіль потрібно налаштувати.\n\nБудь-який користувач пристрою може оновлювати додатки для решти користувачів. Налаштування й сервіси доступності можуть не передаватися новому користувачеві."</string> <string name="user_add_user_message_short" msgid="3295959985795716166">"Користувач має налаштувати свій профіль після створення.\n\nБудь-який користувач пристрою може оновлювати додатки для решти користувачів."</string> <string name="user_grant_admin_title" msgid="5157031020083343984">"Надати цьому користувачу права адміністратора?"</string> <string name="user_grant_admin_message" msgid="1673791931033486709">"Адміністратори, на відміну від звичайних користувачів, мають спеціальні права. Вони можуть керувати всіма користувачами, оновлювати, скидати чи змінювати налаштування цього пристрою, переглядати всі встановлені додатки, а також надавати іншим користувачам права адміністратора або відкликати їх."</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 4991022db5e1..70aa9a3ddbb7 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"ابھی ابھی"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"یہ فون"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"یہ ٹیبلیٹ"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"ڈاک اسپیکر"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"منسلک آلہ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"اس آلے پر چلایا نہیں جا سکتا"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"سوئچ کرنے کے لیے اکاؤنٹ اپ گریڈ کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index d4d0bbc0b869..5f42969a23f1 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -545,13 +545,16 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Hozir"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Shu telefon"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Shu planshet"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Dok-stansiyali karnay"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Tashqi qurilma"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ulangan qurilma"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Shu telefon"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu qurilmada ijro etilmaydi"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Oʻtish uchun hisobingizni yangilang"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Yuklab olingan fayllar ijro etilmaydi"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"Reklamadan keyin qayta urining"</string> <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"Ijro etish uchun qurilmani uyqu rejimidan chiqaring."</string> - <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Qurilmaga ijro etish uchun ruxsat berilmagan"</string> + <string name="media_output_status_unauthorized" msgid="5880222828273853838">"Qurilmada ijro ruxsati yoʻq"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"Media fayl ijro etilmaydi"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 1609d928900b..d75f1b266283 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Vừa xong"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Điện thoại này"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Máy tính bảng này"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Loa có gắn đế"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Thiết bị bên ngoài"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Thiết bị đã kết nối"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Điện thoại này"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Không phát được trên thiết bị này"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nâng cấp tài khoản để chuyển đổi"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 9815c90d81e6..23b6ff3052af 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -455,7 +455,7 @@ <string name="power_discharge_by_only_enhanced" msgid="3268796172652988877">"根据您的使用情况,估计能用到<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by" msgid="4113180890060388350">"目前电量为 <xliff:g id="LEVEL">%2$s</xliff:g>,估计能用到<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_discharge_by_only" msgid="92545648425937000">"估计能用到<xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_discharge_by_only_short" msgid="5883041507426914446">"直到<xliff:g id="TIME">%1$s</xliff:g>"</string> + <string name="power_discharge_by_only_short" msgid="5883041507426914446">"可以用到 <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"电池电量可能在<xliff:g id="TIME">%1$s</xliff:g> 前耗尽"</string> <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> <string name="power_remaining_less_than_duration" msgid="318215464914990578">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string> @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"刚刚"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"这部手机"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"这台平板电脑"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"基座音箱"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"无法在此设备上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"升级帐号后才能切换"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 1412dcdb04ff..e6da4bc6eca5 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -545,12 +545,15 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"此手機"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"此平板電腦"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"插座喇叭"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連接的裝置"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在此裝置上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"無法在此播放下載內容"</string> <string name="media_output_status_try_after_ad" msgid="8312579066856015441">"請在廣告結束後再試一次"</string> - <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"喚醒裝置即可在此播放"</string> + <string name="media_output_status_device_in_low_power_mode" msgid="8184631698321758451">"必須喚醒裝置才能在此播放"</string> <string name="media_output_status_unauthorized" msgid="5880222828273853838">"裝置未獲核准播放"</string> <string name="media_output_status_track_unsupported" msgid="5576313219317709664">"無法在此播放此媒體內容"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 2a04ea9d39a6..e33934cead42 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"這支手機"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"這台平板電腦"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"座架喇叭"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連結的裝置"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在這部裝置上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 80f4d505a294..5c7a7b557f64 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -545,6 +545,9 @@ <string name="time_unit_just_now" msgid="3006134267292728099">"Khona manje"</string> <string name="media_transfer_this_device_name" product="default" msgid="2357329267148436433">"Le foni"</string> <string name="media_transfer_this_device_name" product="tablet" msgid="3714653244000242800">"Le thebulethi"</string> + <string name="media_transfer_dock_speaker_device_name" msgid="2856219597113881950">"Isipikha sentuba"</string> + <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Idivayisi Yangaphandle"</string> + <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Idivayisi exhunyiwe"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ayikwazi ukudlala kule divayisi"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Thuthukisa i-akhawunti ukuze ushintshe"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index dac7f8d15388..b44c0e0799b2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -929,7 +929,7 @@ <!-- UI debug setting: show all ANRs summary [CHAR LIMIT=100] --> <string name="show_all_anrs_summary">Display App Not Responding dialog for background apps</string> - <!-- UI debug setting: show missing channel toasts? [CHAR LIMIT=25] --> + <!-- UI debug setting: show missing channel toasts? [CHAR LIMIT=30] --> <string name="show_notification_channel_warnings">Show notification channel warnings</string> <!-- UI debug setting: show missing channel toasts summary [CHAR LIMIT=50] --> <string name="show_notification_channel_warnings_summary">Displays on-screen warning when an app posts a notification without a valid channel</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 8d4aa9a7b25e..c967b568042c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -688,8 +688,12 @@ public class Utils { continue; } for (int complianceWarningType : complianceWarnings) { - if (complianceWarningType != 0) { - return true; + switch (complianceWarningType) { + case UsbPortStatus.COMPLIANCE_WARNING_OTHER: + case UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY: + return true; + default: + break; } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index fe8988385453..6eb2f3834858 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -484,8 +484,9 @@ public class ApplicationsState { if (DEBUG_LOCKING) Log.v(TAG, "getEntry about to acquire lock..."); synchronized (mEntriesMap) { AppEntry entry = null; - if (mEntriesMap.contains(userId)) { - entry = mEntriesMap.get(userId).get(packageName); + HashMap<String, AppEntry> userEntriesMap = mEntriesMap.get(userId); + if (userEntriesMap != null) { + entry = userEntriesMap.get(packageName); } if (entry == null) { ApplicationInfo info = getAppInfoLocked(packageName, userId); @@ -735,8 +736,9 @@ public class ApplicationsState { private AppEntry getEntryLocked(ApplicationInfo info) { int userId = UserHandle.getUserId(info.uid); AppEntry entry = null; - if (mEntriesMap.contains(userId)) { - entry = mEntriesMap.get(userId).get(info.packageName); + HashMap<String, AppEntry> userEntriesMap = mEntriesMap.get(userId); + if (userEntriesMap != null) { + entry = userEntriesMap.get(info.packageName); } if (DEBUG) { Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry); @@ -752,8 +754,11 @@ public class ApplicationsState { Log.i(TAG, "Creating AppEntry for " + info.packageName); } entry = new AppEntry(mContext, info, mCurId++); - mEntriesMap.get(userId).put(info.packageName, entry); - mAppEntries.add(entry); + userEntriesMap = mEntriesMap.get(userId); + if (userEntriesMap != null) { + userEntriesMap.put(info.packageName, entry); + mAppEntries.add(entry); + } } else if (entry.info != info) { entry.info = info; } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 3fcb7f398185..ffc0479f01c3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -116,10 +116,7 @@ public class InfoMediaManager extends MediaManager { && !TextUtils.isEmpty(mPackageName)) { RouteListingPreference routeListingPreference = mRouterManager.getRouteListingPreference(mPackageName); - if (routeListingPreference != null) { - Api34Impl.onRouteListingPreferenceUpdated(null, routeListingPreference, - mPreferenceItemMap); - } + Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap); } refreshDevices(); } @@ -631,7 +628,7 @@ public class InfoMediaManager extends MediaManager { } /** - * Ignore callback here since we'll also receive {@link onRequestFailed} with reason code. + * Ignore callback here since we'll also receive {@link #onRequestFailed} with reason code. */ @Override public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) { @@ -656,9 +653,9 @@ public class InfoMediaManager extends MediaManager { public void onRouteListingPreferenceUpdated( String packageName, RouteListingPreference routeListingPreference) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { - Api34Impl.onRouteListingPreferenceUpdated(packageName, routeListingPreference, - mPreferenceItemMap); + if (TextUtils.equals(mPackageName, packageName)) { + Api34Impl.onRouteListingPreferenceUpdated( + routeListingPreference, mPreferenceItemMap); refreshDevices(); } } @@ -746,7 +743,6 @@ public class InfoMediaManager extends MediaManager { @DoNotInline static void onRouteListingPreferenceUpdated( - String packageName, RouteListingPreference routeListingPreference, Map<String, RouteListingPreference.Item> preferenceItemMap) { preferenceItemMap.clear(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 0637e5d27f57..4a913c87bddf 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -455,12 +455,24 @@ public class UtilsTest { } @Test - public void containsIncompatibleChargers_returnTrue() { - setupIncompatibleCharging(); + public void containsIncompatibleChargers_complianeWarningOther_returnTrue() { + setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue(); + } + + @Test + public void containsIncompatibleChargers_complianeWarningDebug_returnTrue() { + setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY); assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue(); } @Test + public void containsIncompatibleChargers_unexpectedWarningType_returnFalse() { + setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_BC_1_2); + assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse(); + } + + @Test public void containsIncompatibleChargers_emptyComplianceWarnings_returnFalse() { setupIncompatibleCharging(); when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]); @@ -494,12 +506,17 @@ public class UtilsTest { } private void setupIncompatibleCharging() { + setupIncompatibleCharging(UsbPortStatus.COMPLIANCE_WARNING_OTHER); + } + + private void setupIncompatibleCharging(int complianceWarningType) { final List<UsbPort> usbPorts = new ArrayList<>(); usbPorts.add(mUsbPort); when(mUsbManager.getPorts()).thenReturn(usbPorts); when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus); when(mUsbPort.supportsComplianceWarnings()).thenReturn(true); when(mUsbPortStatus.isConnected()).thenReturn(true); - when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1}); + when(mUsbPortStatus.getComplianceWarnings()) + .thenReturn(new int[]{complianceWarningType}); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java index 1d081d7214cc..34d8148f418f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java @@ -804,7 +804,7 @@ public class ApplicationsStateRoboTest { } @Test - public void getEntry_validUserId_shouldReturnEntry() { + public void getEntry_hasCache_shouldReturnCacheEntry() { mApplicationsState.mEntriesMap.put(/* userId= */ 0, new HashMap<>()); addApp(PKG_1, /* id= */ 1); @@ -813,10 +813,13 @@ public class ApplicationsStateRoboTest { } @Test - public void getEntry_invalidUserId_shouldReturnNull() { - mApplicationsState.mEntriesMap.put(/* userId= */ 0, new HashMap<>()); - addApp(PKG_1, /* id= */ 1); + public void getEntry_hasNoCache_shouldReturnEntry() { + mApplicationsState.mEntriesMap.clear(); + ApplicationInfo appInfo = createApplicationInfo(PKG_1, /* uid= */ 0); + mApplicationsState.mApplications.add(appInfo); + mApplicationsState.mSystemModules.put(PKG_1, /* value= */ false); - assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ -1)).isNull(); + assertThat(mApplicationsState.getEntry(PKG_1, /* userId= */ 0).info.packageName) + .isEqualTo(PKG_1); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index aa5f3df1b750..39780f3a96ed 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -73,6 +73,7 @@ import java.util.Set; public class InfoMediaManagerTest { private static final String TEST_PACKAGE_NAME = "com.test.packagename"; + private static final String TEST_PACKAGE_NAME_2 = "com.test.packagename2"; private static final String TEST_ID = "test_id"; private static final String TEST_ID_1 = "test_id_1"; private static final String TEST_ID_2 = "test_id_2"; @@ -308,7 +309,54 @@ public class InfoMediaManagerTest { } @Test - public void onRouteChanged_getAvailableRoutesWithPrefernceListExit_ordersRoutes() { + public void onRouteChanged_getAvailableRoutesWithPreferenceListExit_ordersRoutes() { + RouteListingPreference routeListingPreference = setUpPreferenceList(TEST_PACKAGE_NAME); + setUpSelectedRoutes(TEST_PACKAGE_NAME); + + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); + routingSessionInfos.add(sessionInfo); + + when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos); + when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID)); + + setAvailableRoutesList(TEST_PACKAGE_NAME); + + mInfoMediaManager.mRouterManager = mRouterManager; + mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME, + routeListingPreference); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); + + assertThat(mInfoMediaManager.mMediaDevices).hasSize(3); + assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID); + assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4); + assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue(); + assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3); + } + + @Test + public void onRouteChanged_preferenceListUpdateWithDifferentPkg_notOrdersRoutes() { + RouteListingPreference routeListingPreference = setUpPreferenceList(TEST_PACKAGE_NAME_2); + setUpSelectedRoutes(TEST_PACKAGE_NAME); + + final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); + final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); + routingSessionInfos.add(sessionInfo); + + when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos); + when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID)); + + setAvailableRoutesList(TEST_PACKAGE_NAME); + mInfoMediaManager.mRouterManager = mRouterManager; + mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME_2, + routeListingPreference); + mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); + + assertThat(mInfoMediaManager.mMediaDevices).hasSize(1); + assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID); + } + + private RouteListingPreference setUpPreferenceList(String packageName) { ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.UPSIDE_DOWN_CAKE); final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>(); @@ -324,57 +372,40 @@ public class InfoMediaManagerTest { RouteListingPreference routeListingPreference = new RouteListingPreference.Builder().setItems( preferenceItemList).setUseSystemOrdering(false).build(); - when(mRouterManager.getRouteListingPreference(TEST_PACKAGE_NAME)) + when(mRouterManager.getRouteListingPreference(packageName)) .thenReturn(routeListingPreference); + return routeListingPreference; + } + private void setUpSelectedRoutes(String packageName) { final List<MediaRoute2Info> selectedRoutes = new ArrayList<>(); final MediaRoute2Info info = mock(MediaRoute2Info.class); when(info.getId()).thenReturn(TEST_ID); - when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(info.getClientPackageName()).thenReturn(packageName); when(info.isSystemRoute()).thenReturn(true); selectedRoutes.add(info); when(mRouterManager.getSelectedRoutes(any())).thenReturn(selectedRoutes); - - final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); - final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); - routingSessionInfos.add(sessionInfo); - - when(mRouterManager.getRoutingSessions(TEST_PACKAGE_NAME)).thenReturn(routingSessionInfos); - when(sessionInfo.getSelectedRoutes()).thenReturn(ImmutableList.of(TEST_ID)); - - setAvailableRoutesList(); - - mInfoMediaManager.mRouterManager = mRouterManager; - mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME, - routeListingPreference); - mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated(); - - assertThat(mInfoMediaManager.mMediaDevices).hasSize(3); - assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID); - assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4); - assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue(); - assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3); } - private List<MediaRoute2Info> setAvailableRoutesList() { + private List<MediaRoute2Info> setAvailableRoutesList(String packageName) { final List<MediaRoute2Info> availableRoutes = new ArrayList<>(); final MediaRoute2Info availableInfo1 = mock(MediaRoute2Info.class); when(availableInfo1.getId()).thenReturn(TEST_ID_2); - when(availableInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(availableInfo1.getClientPackageName()).thenReturn(packageName); when(availableInfo1.getType()).thenReturn(TYPE_REMOTE_TV); availableRoutes.add(availableInfo1); final MediaRoute2Info availableInfo2 = mock(MediaRoute2Info.class); when(availableInfo2.getId()).thenReturn(TEST_ID_3); - when(availableInfo2.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(availableInfo2.getClientPackageName()).thenReturn(packageName); availableRoutes.add(availableInfo2); final MediaRoute2Info availableInfo3 = mock(MediaRoute2Info.class); when(availableInfo3.getId()).thenReturn(TEST_ID_4); - when(availableInfo3.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + when(availableInfo3.getClientPackageName()).thenReturn(packageName); availableRoutes.add(availableInfo3); - when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn( + when(mRouterManager.getAvailableRoutes(packageName)).thenReturn( availableRoutes); return availableRoutes; diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index fe90caf2646c..0aa121dfcc49 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -913,8 +913,9 @@ android:excludeFromRecents="true" android:launchMode="singleInstance" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:visibleToInstantApps="true"> - </activity> + android:visibleToInstantApps="true" + android:exported="true" + /> <activity android:name=".controls.management.ControlsEditingActivity" android:label="@string/controls_menu_edit" @@ -947,8 +948,9 @@ android:finishOnTaskLaunch="true" android:launchMode="singleInstance" android:configChanges="screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|orientation" - android:visibleToInstantApps="true"> - </activity> + android:visibleToInstantApps="true" + android:exported="true" + /> <activity android:name=".wallet.ui.WalletActivity" android:label="@string/wallet_title" @@ -994,7 +996,6 @@ android:name=".notetask.shortcut.LaunchNoteTaskActivity" android:exported="true" android:excludeFromRecents="true" - android:resizeableActivity="false" android:theme="@android:style/Theme.NoDisplay" > <intent-filter> @@ -1010,7 +1011,6 @@ android:exported="false" android:enabled="true" android:excludeFromRecents="true" - android:resizeableActivity="false" android:theme="@android:style/Theme.NoDisplay" /> <activity diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index aee829dbf868..01e6bf00a664 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -1,6 +1,22 @@ { - // Looking for unit test presubmit configuration? - // This currently lives in ATP config apct/system_ui/unit_test + // Curious where your @Scenario tests will run? + // + // @Ignore: Will not run in any configuration + // + // @FlakyTest: Tests that don't block pre/postsubmit but are staged to run known failures + // + // @Postsubmit: Runs in platinum suite and blocks droidfood in postsubmit + // + // @PlatinumTest: As of May, 2023, running in postsubmit. Set to run in presubmit as part of + // v2/android-platinum/suite-test-mapping-platinum-sysui + // Please DO NOT annotate new or old tests with @PlatinumTest annotation without discussing + // with mdb:android-platinum + // + // As of May, 2023, If you don't use @Postsubmit, your new test will immediately + // block presubmit, which is probably NOT what you want. This will change effectively once + // we move to @PlatinumTest annotation. + + // v2/sysui/test-mapping-presubmit-sysui_cloud-tf "presubmit-sysui": [ { "name": "PlatformScenarioTests", @@ -23,6 +39,7 @@ ] } ], + // v2/android-virtual-infra/test_mapping/presubmit-avd "presubmit": [ { "name": "SystemUIGoogleTests", @@ -75,21 +92,6 @@ ] } ], - - // Curious where your @Scenario tests will run? - // - // @Ignore: nowhere - // @FlakyTest: in staged-postsubmit, but not blocking postsubmit or - // presubmit - // @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit - // none of the above: in presubmit, postsubmit, and staged-postsubmit - // - // Ideally, please annotate new tests with @FlakyTest, then with @Postsubmit - // once they're ready for postsubmit as they will immediately block go/android-platinum, - // then with neither once they're ready for presubmit. - // - // If you don't use @Postsubmit, your new test will immediately - // block presubmit, which is probably not what you want! "auto-end-to-end-postsubmit": [ { "name": "AndroidAutomotiveHomeTests", @@ -107,5 +109,27 @@ } ] } + ], + "silver-sysui": [ + { + "name": "PlatformScenarioTests", + "options": [ + { + "include-filter": "android.platform.test.scenario.sysui" + }, + { + "include-annotation": "android.platform.test.scenario.annotation.Scenario" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.PlatinumTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } ] } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml index fb31e1da6361..9d9b05a5e3cb 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-de/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menü für Bedienungshilfen"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"Über das Menü „Bedienungshilfen“ lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Über das Menü für Bedienungshilfen lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistant"</string> <string name="assistant_utterance" msgid="65509599221141377">"Assistant"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Einstellungen für Bedienungshilfen"</string> @@ -20,9 +20,9 @@ <string name="brightness_down_label" msgid="7115662941913272072">"Helligkeit verringern"</string> <string name="previous_button_content_description" msgid="840869171117765966">"Zum vorherigen Bildschirm"</string> <string name="next_button_content_description" msgid="6810058269847364406">"Zum nächsten Bildschirm"</string> - <string name="accessibility_menu_description" msgid="4458354794093858297">"Über das Menü „Bedienungshilfen“ lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> + <string name="accessibility_menu_description" msgid="4458354794093858297">"Über das Menü für Bedienungshilfen lässt sich ein großes Menü zur Bedienung deines Geräts auf dem Bildschirm öffnen. Du kannst beispielsweise das Gerät sperren, die Lautstärke und Helligkeit anpassen und Screenshots machen."</string> <string name="accessibility_menu_summary" msgid="340071398148208130">"Gerät mit großem Menü steuern"</string> - <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Menüeinstellungen f. Bedienungshilfen"</string> + <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Einstellungen für das Menü „Bedienungshilfen“"</string> <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Große Schaltflächen"</string> <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Schaltflächen für das Menü „Bedienungshilfen“ vergrößern"</string> <string name="pref_help_title" msgid="6871558837025010641">"Hilfe"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml index 1ae3213639e1..eff6cb45309b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-hr/strings.xml @@ -9,7 +9,7 @@ <string name="power_label" msgid="7699720321491287839">"Napajanje"</string> <string name="power_utterance" msgid="7444296686402104807">"Opcije napajanja"</string> <string name="recent_apps_label" msgid="6583276995616385847">"Nedavne aplikacije"</string> - <string name="lockscreen_label" msgid="648347953557887087">"Zaključan zaslon"</string> + <string name="lockscreen_label" msgid="648347953557887087">"Zaključani zaslon"</string> <string name="quick_settings_label" msgid="2999117381487601865">"Brze postavke"</string> <string name="notifications_label" msgid="6829741046963013567">"Obavijesti"</string> <string name="screenshot_label" msgid="863978141223970162">"Snimka zaslona"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml index 9726f2056a36..48c2634191d9 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-kk/strings.xml @@ -11,7 +11,7 @@ <string name="recent_apps_label" msgid="6583276995616385847">"Соңғы қолданбалар"</string> <string name="lockscreen_label" msgid="648347953557887087">"Құлып экраны"</string> <string name="quick_settings_label" msgid="2999117381487601865">"Жылдам параметрлер"</string> - <string name="notifications_label" msgid="6829741046963013567">"Хабарланды00ADрулар"</string> + <string name="notifications_label" msgid="6829741046963013567">"Хабарландырулар"</string> <string name="screenshot_label" msgid="863978141223970162">"Скриншот"</string> <string name="screenshot_utterance" msgid="1430760563401895074">"Скриншот жасау"</string> <string name="volume_up_label" msgid="8592766918780362870">"Дыбысын арттыру"</string> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt index 83e44b69812b..addabcc0282a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt @@ -18,9 +18,9 @@ package com.android.systemui.animation import android.graphics.fonts.Font import android.graphics.fonts.FontVariationAxis +import android.util.Log import android.util.LruCache import android.util.MathUtils -import android.util.MathUtils.abs import androidx.annotation.VisibleForTesting import java.lang.Float.max import java.lang.Float.min @@ -29,8 +29,6 @@ private const val TAG_WGHT = "wght" private const val TAG_ITAL = "ital" private const val FONT_WEIGHT_DEFAULT_VALUE = 400f -private const val FONT_WEIGHT_ANIMATION_FRAME_COUNT = 100 - private const val FONT_ITALIC_MAX = 1f private const val FONT_ITALIC_MIN = 0f private const val FONT_ITALIC_ANIMATION_STEP = 0.1f @@ -38,11 +36,12 @@ private const val FONT_ITALIC_DEFAULT_VALUE = 0f // Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in // frame draw time on a Pixel 6. -@VisibleForTesting const val FONT_CACHE_MAX_ENTRIES = 10 +@VisibleForTesting const val DEFAULT_FONT_CACHE_MAX_ENTRIES = 10 /** Provide interpolation of two fonts by adjusting font variation settings. */ -class FontInterpolator { - +class FontInterpolator( + numberOfAnimationSteps: Int? = null, +) { /** * Cache key for the interpolated font. * @@ -87,8 +86,9 @@ class FontInterpolator { // Font interpolator has two level caches: one for input and one for font with different // variation settings. No synchronization is needed since FontInterpolator is not designed to be // thread-safe and can be used only on UI thread. - private val interpCache = LruCache<InterpKey, Font>(FONT_CACHE_MAX_ENTRIES) - private val verFontCache = LruCache<VarFontKey, Font>(FONT_CACHE_MAX_ENTRIES) + val cacheMaxEntries = numberOfAnimationSteps?.let { it * 2 } ?: DEFAULT_FONT_CACHE_MAX_ENTRIES + private val interpCache = LruCache<InterpKey, Font>(cacheMaxEntries) + private val verFontCache = LruCache<VarFontKey, Font>(cacheMaxEntries) // Mutable keys for recycling. private val tmpInterpKey = InterpKey(null, null, 0f) @@ -114,6 +114,9 @@ class FontInterpolator { tmpInterpKey.set(start, end, progress) val cachedFont = interpCache[tmpInterpKey] if (cachedFont != null) { + if (DEBUG) { + Log.d(LOG_TAG, "[$progress] Interp. cache hit for $tmpInterpKey") + } return cachedFont } @@ -124,18 +127,12 @@ class FontInterpolator { val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> when (tag) { - // TODO: Good to parse 'fvar' table for retrieving default value. - TAG_WGHT -> { - adaptiveAdjustWeight( - MathUtils.lerp( - startValue ?: FONT_WEIGHT_DEFAULT_VALUE, - endValue ?: FONT_WEIGHT_DEFAULT_VALUE, - progress - ), + TAG_WGHT -> + MathUtils.lerp( startValue ?: FONT_WEIGHT_DEFAULT_VALUE, endValue ?: FONT_WEIGHT_DEFAULT_VALUE, + progress ) - } TAG_ITAL -> adjustItalic( MathUtils.lerp( @@ -159,6 +156,9 @@ class FontInterpolator { val axesCachedFont = verFontCache[tmpVarFontKey] if (axesCachedFont != null) { interpCache.put(InterpKey(start, end, progress), axesCachedFont) + if (DEBUG) { + Log.d(LOG_TAG, "[$progress] Axis cache hit for $tmpVarFontKey") + } return axesCachedFont } @@ -168,6 +168,9 @@ class FontInterpolator { val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build() interpCache.put(InterpKey(start, end, progress), newFont) verFontCache.put(VarFontKey(start, newAxes), newFont) + + // Cache misses are likely to create memory leaks, so this is logged at error level. + Log.e(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey") return newFont } @@ -215,15 +218,6 @@ class FontInterpolator { return result } - // For the performance reasons, we animate weight with adaptive step. This helps - // Cache hit ratio in the Skia glyph cache. - // The reason we don't use fix step is because the range of weight axis is not normalized, - // some are from 50 to 100, others are from 0 to 1000, so we cannot give a constant proper step - private fun adaptiveAdjustWeight(value: Float, start: Float, end: Float): Float { - val step = max(abs(end - start) / FONT_WEIGHT_ANIMATION_FRAME_COUNT, 1F) - return coerceInWithStep(value, min(start, end), max(start, end), step) - } - // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps // Cache hit ratio in the Skia glyph cache. private fun adjustItalic(value: Float) = @@ -233,6 +227,8 @@ class FontInterpolator { (v.coerceIn(min, max) / step).toInt() * step companion object { + private const val LOG_TAG = "FontInterpolator" + private val DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG) private val EMPTY_AXES = arrayOf<FontVariationAxis>() // Returns true if given two font instance can be interpolated. diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt index 00108940e6ec..b555fa583588 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt @@ -26,6 +26,7 @@ import android.graphics.fonts.Font import android.graphics.fonts.FontVariationAxis import android.text.Layout import android.util.LruCache +import kotlin.math.roundToInt private const val DEFAULT_ANIMATION_DURATION: Long = 300 private const val TYPEFACE_CACHE_MAX_ENTRIES = 5 @@ -63,9 +64,9 @@ class TypefaceVariantCacheImpl( return it } - return TypefaceVariantCache - .createVariantTypeface(baseTypeface, fvar) - .also { cache.put(fvar, it) } + return TypefaceVariantCache.createVariantTypeface(baseTypeface, fvar).also { + cache.put(fvar, it) + } } } @@ -74,7 +75,6 @@ class TypefaceVariantCacheImpl( * * Currently this class can provide text style animation for text weight and text size. For example * the simple view that draws text with animating text size is like as follows: - * * <pre> <code> * ``` * class SimpleTextAnimation : View { @@ -97,6 +97,7 @@ class TypefaceVariantCacheImpl( */ class TextAnimator( layout: Layout, + numberOfAnimationSteps: Int? = null, // Only do this number of discrete animation steps. private val invalidateCallback: () -> Unit, ) { var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl(layout.paint.typeface) @@ -107,12 +108,14 @@ class TextAnimator( } // Following two members are for mutable for testing purposes. - public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache) + public var textInterpolator: TextInterpolator = + TextInterpolator(layout, typefaceCache, numberOfAnimationSteps) public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION addUpdateListener { - textInterpolator.progress = it.animatedValue as Float + textInterpolator.progress = + calculateProgress(it.animatedValue as Float, numberOfAnimationSteps) invalidateCallback() } addListener( @@ -123,6 +126,17 @@ class TextAnimator( ) } + private fun calculateProgress(animProgress: Float, numberOfAnimationSteps: Int?): Float { + if (numberOfAnimationSteps != null) { + // This clamps the progress to the nearest value of "numberOfAnimationSteps" + // discrete values between 0 and 1f. + return (animProgress * numberOfAnimationSteps).roundToInt() / + numberOfAnimationSteps.toFloat() + } + + return animProgress + } + sealed class PositionedGlyph { /** Mutable X coordinate of the glyph position relative from drawing offset. */ diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index 8ed8d8fb61fd..02caeeddd774 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -31,6 +31,7 @@ import java.lang.Math.max class TextInterpolator( layout: Layout, var typefaceCache: TypefaceVariantCache, + numberOfAnimationSteps: Int? = null, ) { /** * Returns base paint used for interpolation. @@ -85,7 +86,7 @@ class TextInterpolator( private class Line(val runs: List<Run>) private var lines = listOf<Line>() - private val fontInterpolator = FontInterpolator() + private val fontInterpolator = FontInterpolator(numberOfAnimationSteps) // Recycling object for glyph drawing and tweaking. private val tmpPaint = TextPaint() diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt index 89871fa7d875..2cd587ffbc45 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt @@ -56,7 +56,19 @@ data class TurbulenceNoiseAnimationConfig( val easeOutDuration: Float = DEFAULT_EASING_DURATION_IN_MILLIS, val pixelDensity: Float = 1f, val blendMode: BlendMode = DEFAULT_BLEND_MODE, - val onAnimationEnd: Runnable? = null + val onAnimationEnd: Runnable? = null, + /** + * Variants in noise. Higher number means more contrast; lower number means less contrast but + * make the noise dimmed. You may want to increase the [lumaMatteBlendFactor] to compensate. + * Expected range [0, 1]. + */ + val lumaMatteBlendFactor: Float = DEFAULT_LUMA_MATTE_BLEND_FACTOR, + /** + * Offset for the overall brightness in noise. Higher number makes the noise brighter. You may + * want to use this if you have made the noise softer using [lumaMatteBlendFactor]. Expected + * range [0, 1]. + */ + val lumaMatteOverallBrightness: Float = DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS ) { companion object { const val DEFAULT_MAX_DURATION_IN_MILLIS = 30_000f // Max 30 sec @@ -66,6 +78,8 @@ data class TurbulenceNoiseAnimationConfig( const val DEFAULT_NOISE_SPEED_Z = 0.3f const val DEFAULT_OPACITY = 150 // full opacity is 255. const val DEFAULT_COLOR = Color.WHITE + const val DEFAULT_LUMA_MATTE_BLEND_FACTOR = 1f + const val DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS = 0f const val DEFAULT_BACKGROUND_COLOR = Color.BLACK val DEFAULT_BLEND_MODE = BlendMode.SRC_OVER } diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt index d1ba7c4de35c..d3c57c91405a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseShader.kt @@ -37,6 +37,8 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uniform float in_opacity; uniform float in_pixelDensity; uniform float in_inverseLuma; + uniform half in_lumaMatteBlendFactor; + uniform half in_lumaMatteOverallBrightness; layout(color) uniform vec4 in_color; layout(color) uniform vec4 in_backgroundColor; """ @@ -48,18 +50,21 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = abs(in_inverseLuma - simplex3d(noiseP)) * in_opacity; + // Bring it to [0, 1] range. + float luma = (simplex3d(noiseP) * in_inverseLuma) * 0.5 + 0.5; + luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) + * in_opacity; vec3 mask = maskLuminosity(in_color.rgb, luma); vec3 color = in_backgroundColor.rgb + mask * 0.6; - // Add dither with triangle distribution to avoid color banding. Ok to dither in the + // Add dither with triangle distribution to avoid color banding. Dither in the // shader here as we are in gamma space. float dither = triangleNoise(p * in_pixelDensity) / 255.; // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to // multiply rgb with a to get the correct result. - color = (color + dither.rrr) * in_color.a; - return vec4(color, in_color.a); + color = (color + dither.rrr) * in_opacity; + return vec4(color, in_opacity); } """ @@ -70,12 +75,15 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : uv.x *= in_aspectRatio; vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum; - float luma = abs(in_inverseLuma - simplex3d_fractal(noiseP)) * in_opacity; + // Bring it to [0, 1] range. + float luma = (simplex3d_fractal(noiseP) * in_inverseLuma) * 0.5 + 0.5; + luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness) + * in_opacity; vec3 mask = maskLuminosity(in_color.rgb, luma); vec3 color = in_backgroundColor.rgb + mask * 0.6; // Skip dithering. - return vec4(color * in_color.a, in_color.a); + return vec4(color * in_opacity, in_opacity); } """ @@ -125,6 +133,28 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : } /** + * Sets blend and brightness factors of the luma matte. + * + * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting + * this a lower number removes variations. I.e. the turbulence noise will look more blended. + * Expected input range is [0, 1]. more dimmed. + * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise. + * Expected input range is [0, 1]. + * + * Example usage: You may want to apply a small number to [lumaMatteBlendFactor], such as 0.2, + * which makes the noise look softer. However it makes the overall noise look dim, so you want + * offset something like 0.3 for [lumaMatteOverallBrightness] to bring back its overall + * brightness. + */ + fun setLumaMatteFactors( + lumaMatteBlendFactor: Float = 1f, + lumaMatteOverallBrightness: Float = 0f + ) { + setFloatUniform("in_lumaMatteBlendFactor", lumaMatteBlendFactor) + setFloatUniform("in_lumaMatteOverallBrightness", lumaMatteOverallBrightness) + } + + /** * Sets whether to inverse the luminosity of the noise. * * By default noise will be used as a luma matte as is. This means that you will see color in @@ -132,7 +162,7 @@ class TurbulenceNoiseShader(useFractal: Boolean = false) : * true. */ fun setInverseNoiseLuminosity(inverse: Boolean) { - setFloatUniform("in_inverseLuma", if (inverse) 1f else 0f) + setFloatUniform("in_inverseLuma", if (inverse) -1f else 1f) } /** Current noise movements in x, y, and z axes. */ diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt index c3e84787d4fb..43d6504fce84 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseView.kt @@ -215,10 +215,12 @@ class TurbulenceNoiseView(context: Context?, attrs: AttributeSet?) : View(contex noiseConfig = config with(turbulenceNoiseShader) { setGridCount(config.gridCount) - setColor(ColorUtils.setAlphaComponent(config.color, config.opacity)) + setColor(config.color) setBackgroundColor(config.backgroundColor) setSize(config.width, config.height) setPixelDensity(config.pixelDensity) + setInverseNoiseLuminosity(inverse = false) + setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness) } paint.blendMode = config.blendMode } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index 465b73e6de19..648ef03895cd 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -74,7 +74,8 @@ class AnimatableClockView @JvmOverloads constructor( private var onTextAnimatorInitialized: Runnable? = null @VisibleForTesting var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = - { layout, invalidateCb -> TextAnimator(layout, invalidateCb) } + { layout, invalidateCb -> + TextAnimator(layout, NUM_CLOCK_FONT_ANIMATION_STEPS, invalidateCb) } @VisibleForTesting var isAnimationEnabled: Boolean = true @VisibleForTesting var timeOverrideInMillis: Long? = null @@ -567,6 +568,7 @@ class AnimatableClockView @JvmOverloads constructor( private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500 private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000 private const val COLOR_ANIM_DURATION: Long = 400 + private const val NUM_CLOCK_FONT_ANIMATION_STEPS = 30 // Constants for the animation private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 0e20444347fd..8cba2ab0b70b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -48,6 +48,18 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext private val KEY_TIMESTAMP = "appliedTimestamp" +private val KNOWN_PLUGINS = + mapOf<String, List<ClockMetadata>>( + "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")), + "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")), + "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")), + "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")), + "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")), + "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")), + "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")), + "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")), + "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")), + ) private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut( key: TKey, @@ -127,8 +139,61 @@ open class ClockRegistry( private val pluginListener = object : PluginListener<ClockProviderPlugin> { - override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) { - manager.loadPlugin() + override fun onPluginAttached( + manager: PluginLifecycleManager<ClockProviderPlugin> + ): Boolean { + if (keepAllLoaded) { + // Always load new plugins if requested + return true + } + + val knownClocks = KNOWN_PLUGINS.get(manager.getPackage()) + if (knownClocks == null) { + logBuffer.tryLog( + TAG, + LogLevel.WARNING, + { str1 = manager.getPackage() }, + { "Loading unrecognized clock package: $str1" } + ) + return true + } + + logBuffer.tryLog( + TAG, + LogLevel.INFO, + { str1 = manager.getPackage() }, + { "Skipping initial load of known clock package package: $str1" } + ) + + var isClockListChanged = false + for (metadata in knownClocks) { + val id = metadata.clockId + val info = + availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) { + isClockListChanged = true + onConnected(id) + } + + if (manager != info.manager) { + logBuffer.tryLog( + TAG, + LogLevel.ERROR, + { str1 = id }, + { "Clock Id conflict on known attach: $str1 is double registered" } + ) + continue + } + + info.provider = null + } + + if (isClockListChanged) { + triggerOnAvailableClocksChanged() + } + verifyLoadedProviders() + + // Load executed via verifyLoadedProviders + return false } override fun onPluginLoaded( diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt index cd9fb886a4e6..e0506046ee54 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt @@ -148,11 +148,11 @@ interface CustomizationProviderClient { */ val isEnabled: Boolean = true, /** - * If the affordance is disabled, this is a set of instruction messages to be shown to the - * user when the disabled affordance is selected. The instructions should help the user - * figure out what to do in order to re-neable this affordance. + * If the affordance is disabled, this is the explanation to be shown to the user when the + * disabled affordance is selected. The instructions should help the user figure out what to + * do in order to re-neable this affordance. */ - val enablementInstructions: List<String>? = null, + val enablementExplanation: String? = null, /** * If the affordance is disabled, this is a label for a button shown together with the set * of instruction messages when the disabled affordance is selected. The button should help @@ -163,15 +163,14 @@ interface CustomizationProviderClient { */ val enablementActionText: String? = null, /** - * If the affordance is disabled, this is a "component name" of the format - * `packageName/action` to be used as an `Intent` for `startActivity` when the action button - * (shown together with the set of instruction messages when the disabled affordance is - * selected) is clicked by the user. The button should help send the user to a flow that - * would help them achieve the instructions and re-enable this affordance. + * If the affordance is disabled, this is an [Intent] to be used with `startActivity` when + * the action button (shown together with the set of instruction messages when the disabled + * affordance is selected) is clicked by the user. The button should help send the user to a + * flow that would help them achieve the instructions and re-enable this affordance. * * If `null`, the button should not be shown. */ - val enablementActionComponentName: String? = null, + val enablementActionIntent: Intent? = null, /** Optional [Intent] to use to start an activity to configure this affordance. */ val configureIntent: Intent? = null, ) @@ -327,20 +326,20 @@ class CustomizationProviderClientImpl( Contract.LockScreenQuickAffordances.AffordanceTable.Columns .IS_ENABLED ) - val enablementInstructionsColumnIndex = + val enablementExplanationColumnIndex = cursor.getColumnIndex( Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_INSTRUCTIONS + .ENABLEMENT_EXPLANATION ) val enablementActionTextColumnIndex = cursor.getColumnIndex( Contract.LockScreenQuickAffordances.AffordanceTable.Columns .ENABLEMENT_ACTION_TEXT ) - val enablementComponentNameColumnIndex = + val enablementActionIntentColumnIndex = cursor.getColumnIndex( Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_COMPONENT_NAME + .ENABLEMENT_ACTION_INTENT ) val configureIntentColumnIndex = cursor.getColumnIndex( @@ -352,9 +351,9 @@ class CustomizationProviderClientImpl( nameColumnIndex == -1 || iconColumnIndex == -1 || isEnabledColumnIndex == -1 || - enablementInstructionsColumnIndex == -1 || + enablementExplanationColumnIndex == -1 || enablementActionTextColumnIndex == -1 || - enablementComponentNameColumnIndex == -1 || + enablementActionIntentColumnIndex == -1 || configureIntentColumnIndex == -1 ) { return@buildList @@ -368,21 +367,22 @@ class CustomizationProviderClientImpl( name = cursor.getString(nameColumnIndex), iconResourceId = cursor.getInt(iconColumnIndex), isEnabled = cursor.getInt(isEnabledColumnIndex) == 1, - enablementInstructions = - cursor - .getString(enablementInstructionsColumnIndex) - ?.split( - Contract.LockScreenQuickAffordances.AffordanceTable - .ENABLEMENT_INSTRUCTIONS_DELIMITER - ), + enablementExplanation = + cursor.getString(enablementExplanationColumnIndex), enablementActionText = cursor.getString(enablementActionTextColumnIndex), - enablementActionComponentName = - cursor.getString(enablementComponentNameColumnIndex), + enablementActionIntent = + cursor + .getString(enablementActionIntentColumnIndex) + ?.toIntent( + affordanceId = affordanceId, + ), configureIntent = cursor .getString(configureIntentColumnIndex) - ?.toIntent(affordanceId = affordanceId), + ?.toIntent( + affordanceId = affordanceId, + ), ) ) } @@ -524,7 +524,7 @@ class CustomizationProviderClientImpl( affordanceId: String, ): Intent? { return try { - Intent.parseUri(this, 0) + Intent.parseUri(this, Intent.URI_INTENT_SCHEME) } catch (e: URISyntaxException) { Log.w(TAG, "Cannot parse Uri into Intent for affordance with ID \"$affordanceId\"!") null diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt index f9e8aafcfa47..b6d5ef3720f5 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt @@ -81,8 +81,6 @@ object CustomizationProviderContract { const val TABLE_NAME = "affordances" val URI: Uri = LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build() - const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "][" - const val COMPONENT_NAME_SEPARATOR = "/" object Columns { /** String. Unique ID for this affordance. */ @@ -97,22 +95,21 @@ object CustomizationProviderContract { /** Integer. `1` if the affordance is enabled or `0` if it disabled. */ const val IS_ENABLED = "is_enabled" /** - * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be - * shown to the user if the affordance is disabled and the user selects the - * affordance. + * String. Text to be shown to the user if the affordance is disabled and the user + * selects the affordance. */ - const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions" + const val ENABLEMENT_EXPLANATION = "enablement_explanation" /** * String. Optional label for a button that, when clicked, opens a destination * activity where the user can re-enable the disabled affordance. */ const val ENABLEMENT_ACTION_TEXT = "enablement_action_text" /** - * String. Optional package name and activity action string, delimited by - * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that - * opens a destination where the user can re-enable the disabled affordance. + * String. Optional URI-formatted `Intent` (formatted using + * `Intent#toUri(Intent.URI_INTENT_SCHEME)` used to start an activity that opens a + * destination where the user can re-enable the disabled affordance. */ - const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent" + const val ENABLEMENT_ACTION_INTENT = "enablement_action_intent" /** * Byte array. Optional parcelled `Intent` to use to start an activity that can be * used to configure the affordance. diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt index 6f363a4ffa26..08ee60204178 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt @@ -20,8 +20,6 @@ package com.android.systemui.shared.quickaffordance.shared.model object KeyguardPreviewConstants { const val MESSAGE_ID_HIDE_SMART_SPACE = 1111 const val KEY_HIDE_SMART_SPACE = "hide_smart_space" - const val MESSAGE_ID_COLOR_OVERRIDE = 1234 - const val KEY_COLOR_OVERRIDE = "color_override" // ColorInt Encoded as string const val MESSAGE_ID_SLOT_SELECTED = 1337 const val KEY_SLOT_ID = "slot_id" const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index 3ae328e67e3d..537b7a41a898 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -190,7 +190,9 @@ enum class ClockTickRate(val value: Int) { data class ClockMetadata( val clockId: ClockId, val name: String, -) +) { + constructor(clockId: ClockId) : this(clockId, clockId) {} +} /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java index cc6a46fa7d6b..56c3f93d49b4 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java @@ -16,12 +16,20 @@ package com.android.systemui.plugins; +import android.content.ComponentName; + /** * Provides the ability for consumers to control plugin lifecycle. * * @param <T> is the target plugin type */ public interface PluginLifecycleManager<T extends Plugin> { + /** Returns the ComponentName of the target plugin. Maybe be called when not loaded. */ + ComponentName getComponentName(); + + /** Returns the package name of the target plugin. May be called when not loaded. */ + String getPackage(); + /** Returns the currently loaded plugin instance (if plugin is loaded) */ T getPlugin(); diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java index c5f503216101..bd0bd8942d5e 100644 --- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java +++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java @@ -60,13 +60,18 @@ public interface PluginListener<T extends Plugin> { /** * Called when the plugin is first attached to the host application. {@link #onPluginLoaded} - * will be automatically called as well when first attached. This may be called multiple times - * if multiple plugins are allowed. It may also be called in the future if the plugin package - * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new - * or different {@link PluginLifecycleManager}. + * will be automatically called as well when first attached if true is returned. This may be + * called multiple times if multiple plugins are allowed. It may also be called in the future + * if the plugin package changes and needs to be reloaded. Each call to + * {@link #onPluginAttached} will provide a new or different {@link PluginLifecycleManager}. + * + * @return returning true will immediately load the plugin and call onPluginLoaded with the + * created object. false will skip loading, but the listener can load it at any time using the + * provided PluginLifecycleManager. Loading plugins immediately is the default behavior. */ - default void onPluginAttached(PluginLifecycleManager<T> manager) { + default boolean onPluginAttached(PluginLifecycleManager<T> manager) { // Optional + return true; } /** diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index 3fc0965f4a81..fc9c917c152b 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -43,7 +43,7 @@ <!-- Not quite optimal but needed to translate these items as a group. The NotificationIconContainer has its own logic for translation. --> - <LinearLayout + <com.android.keyguard.KeyguardStatusAreaView android:id="@+id/keyguard_status_area" android:orientation="vertical" android:layout_width="match_parent" @@ -63,5 +63,5 @@ android:paddingStart="@dimen/below_clock_padding_start_icons" android:visibility="invisible" /> - </LinearLayout> + </com.android.keyguard.KeyguardStatusAreaView> </com.android.keyguard.KeyguardClockSwitch> diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml index 3770c7d5fb27..5d8c508d20c1 100644 --- a/packages/SystemUI/res-keyguard/values-bs/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bs/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">"Unesite svoj PIN"</string> + <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Unesite PIN"</string> <string name="keyguard_enter_pin" msgid="8114529922480276834">"Unesite PIN"</string> <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Unesite uzorak"</string> <string name="keyguard_enter_pattern" msgid="7616595160901084119">"Unesite uzorak"</string> @@ -98,8 +98,8 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Otključavanje SIM-a…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Unesite PIN koji sadrži 4 do 8 brojeva."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK kôd treba sadržavati najmanje 8 brojeva."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Pogrešno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Pogrešno ste unijeli lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Pogrešno ste napisali PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Pogrešno ste napisali lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Pogrešno ste nacrtali svoj uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"PIN za SIM karticu je netačan. Za otključavanje uređaja sada se morate obratiti svom operateru."</string> <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{PIN kôd za SIM je netačan. Imate još # pokušaj prije nego što budete morali kontaktirati mobilnog operatera da vam otključa uređaj.}one{PIN kôd za SIM je netačan. Imate još # pokušaj. }few{PIN kôd za SIM je netačan. Imate još # pokušaja. }other{PIN kôd za SIM je netačan. Imate još # pokušaja. }}"</string> diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml index 8b31fe05e5d6..04b6a3d7bfd8 100644 --- a/packages/SystemUI/res-keyguard/values-ca/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml @@ -98,8 +98,8 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"S\'està desbloquejant la targeta SIM…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Escriu un PIN que tingui entre 4 i 8 números."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"El codi PUK ha de tenir 8 números o més."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Has escrit el PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Has escrit la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%1$d</xliff:g> vegades de manera incorrecta. \n\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%2$d</xliff:g> segons."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"El codi PIN de la SIM no és correcte. Contacta amb l\'operador de telefonia mòbil per desbloquejar el dispositiu."</string> <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{El codi PIN de la SIM no és correcte. Et queda # intent; si no l\'encertes, contacta amb l\'operador per desbloquejar el dispositiu.}many{El codi PIN de la SIM no és correcte. Et queden # intents. }other{El codi PIN de la SIM no és correcte. Et queden # intents. }}"</string> diff --git a/packages/SystemUI/res-keyguard/values-el/strings.xml b/packages/SystemUI/res-keyguard/values-el/strings.xml index 23e566816a41..d042b78060ea 100644 --- a/packages/SystemUI/res-keyguard/values-el/strings.xml +++ b/packages/SystemUI/res-keyguard/values-el/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Ξεκλείδωμα SIM…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Πληκτρολογήστε έναν αριθμό PIN που να αποτελείται από 4 έως 8 αριθμούς."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Ο κωδικός PUK θα πρέπει να περιέχει τουλάχιστον 8 αριθμούς."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Έχετε πληκτρολογήσει τον αριθμό PIN εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Έχετε πληκτρολογήσει το PIN σας εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος<xliff:g id="NUMBER_0">%1$d</xliff:g> φορές. \n\nΠροσπαθήστε ξανά σε <xliff:g id="NUMBER_1">%2$d</xliff:g> δευτερόλεπτα."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Λανθασμένος κωδικός PIN κάρτας SIM. Θα πρέπει να επικοινωνήσετε με την εταιρεία κινητής τηλεφωνίας σας για να ξεκλειδώσετε τη συσκευή σας."</string> diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml index b0dc3bec53c9..4d4c3f9d826e 100644 --- a/packages/SystemUI/res-keyguard/values-eu/strings.xml +++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml @@ -92,7 +92,7 @@ <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Idatzi \"<xliff:g id="CARRIER">%1$s</xliff:g>\" operadorearen SIM txartelaren PINa."</string> <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desgaitu eSIM txartela gailua zerbitzu mugikorrik gabe erabiltzeko."</string> <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. Xehetasunak lortzeko, jarri operadorearekin harremanetan."</string> - <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Desgaitu egin da \"<xliff:g id="CARRIER">%1$s</xliff:g>\" operadorearen SIM txartela. Aurrera egiteko, idatzi PUK kodea. Xehetasunak jakiteko, jarri operadorearekin harremanetan."</string> + <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Desgaitu egin da \"<xliff:g id="CARRIER">%1$s</xliff:g>\" operadorearen SIMa. Aurrera egiteko, idatzi PUK kodea. Xehetasunak jakiteko, jarri operadorearekin harremanetan."</string> <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Idatzi erabili nahi duzun PIN kodea"</string> <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Berretsi erabili nahi duzun PIN kodea"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIMa desblokeatzen…"</string> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index 52b204f54e63..10c7aafd9ab2 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"सिम अनलॉक हो रहा है…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"कोई ऐसा पिन लिखें, जिसमें 4 से 8 अंक हों."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK कोड 8 या ज़्यादा संख्या वाला होना चाहिए."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"आप अपना पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से कोशिश करें."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"<xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत पिन डाला जा चुका है. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से कोशिश करें."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से लिख चुके हैं. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से कोशिश करें."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"आपने अपने लॉक खोलने के पैटर्न को <xliff:g id="NUMBER_0">%1$d</xliff:g> बार गलत तरीके से ड्रॉ किया है. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंड में फिर से कोशिश करें."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"गलत SIM पिन कोड, अपने डिवाइस को अनलॉक करने के लिए अब आपको अपनी मोबाइल और इंटरनेट सेवा देने वाली कंपनी से संपर्क करना होगा."</string> diff --git a/packages/SystemUI/res-keyguard/values-hr/strings.xml b/packages/SystemUI/res-keyguard/values-hr/strings.xml index b5034d8cb64c..045c9045563d 100644 --- a/packages/SystemUI/res-keyguard/values-hr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hr/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Otključavanje SIM-a…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Unesite PIN koji ima od 4 do 8 brojeva."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK kôd treba imati 8 brojeva ili više."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Netočno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> put/a. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Netočno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Netočno ste unijeli zaporku <xliff:g id="NUMBER_0">%1$d</xliff:g> put/a. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Netočno ste iscrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> put/a. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Netočan PIN kôd SIM kartice; sada morate kontaktirati svog mobilnog operatera da bi otključao vaš uređaj."</string> diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml index bc8fd4064c85..8b0d60ce2555 100644 --- a/packages/SystemUI/res-keyguard/values-kn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml @@ -87,7 +87,7 @@ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"ತಪ್ಪಾದ ಪಿನ್ನೊಂದಿಗೆ ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ"</string> <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"ತಪ್ಪಾದ ಪ್ಯಾಟರ್ನ್ನೊಂದಿಗೆ ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ"</string> <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"ತಪ್ಪಾದ ಪಾಸ್ವರ್ಡ್ನೊಂದಿಗೆ ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿರುವಿರಿ"</string> - <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# ಸೆಕೆಂಡಿನಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.}one{# ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.}other{# ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.}}"</string> + <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# ಸೆಕೆಂಡಿನ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.}one{# ಸೆಕೆಂಡುಗಳ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.}other{# ಸೆಕೆಂಡುಗಳ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ.}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ."</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" ಗಾಗಿ ಸಿಮ್ ಪಿನ್ ನಮೂದಿಸಿ."</string> <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> ಮೊಬೈಲ್ ಸೇವೆ ಇಲ್ಲದೆ ಸಾಧನವನ್ನು ಬಳಸಲು eSIM ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ."</string> @@ -98,9 +98,9 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4 ರಿಂದ 8 ಸಂಖ್ಯೆಗಳಿರುವ ಪಿನ್ ಟೈಪ್ ಮಾಡಿ."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK ಕೋಡ್ 8 ಅಥವಾ ಹೆಚ್ಚು ಸಂಖ್ಯೆಗಳನ್ನು ಹೊಂದಿರಬೇಕು."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"ನಿಮ್ಮ ಪಿನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ್ದೀರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ನಮೂದಿಸಿದ್ದೀರಿ. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಎಳೆದಿದ್ದೀರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"ನಿಮ್ಮ ಪಿನ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ್ದೀರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ನೀವು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಟೈಪ್ ಮಾಡಿದ್ದೀರಿ. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> + <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ನಿಮ್ಮ ಅನ್ಲಾಕ್ ಪ್ಯಾಟರ್ನ್ ಅನ್ನು <xliff:g id="NUMBER_0">%1$d</xliff:g> ಬಾರಿ ತಪ್ಪಾಗಿ ಎಳೆದಿದ್ದೀರಿ. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ಸಿಮ್ ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ನೀವು ಈ ಕೂಡಲೇ ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಬೇಕು."</string> <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್ಲಾಕ್ ಮಾಡುವುದಕ್ಕಾಗಿ ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸುವ ಮುನ್ನ ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನ ಬಾಕಿ ಉಳಿದಿದೆ.}one{SIM ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. }other{SIM ಪಿನ್ ಕೋಡ್ ತಪ್ಪಾಗಿದೆ, ನಿಮ್ಮಲ್ಲಿ # ಪ್ರಯತ್ನಗಳು ಬಾಕಿ ಉಳಿದಿವೆ. }}"</string> <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"ಸಿಮ್ ನಿಷ್ಪ್ರಯೋಜಕವಾಗಿದೆ. ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ."</string> diff --git a/packages/SystemUI/res-keyguard/values-ko/strings.xml b/packages/SystemUI/res-keyguard/values-ko/strings.xml index 5fadaabc5e3d..cfd053c12d44 100644 --- a/packages/SystemUI/res-keyguard/values-ko/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ko/strings.xml @@ -58,7 +58,7 @@ <string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"잘못된 패턴입니다. 다시 시도해 주세요."</string> <string name="kg_wrong_password" msgid="4143127991071670512">"잘못된 비밀번호"</string> <string name="kg_wrong_password_try_again" msgid="6602878676125765920">"잘못된 비밀번호입니다. 다시 시도해 주세요."</string> - <string name="kg_wrong_pin" msgid="4160978845968732624">"PIN 오류"</string> + <string name="kg_wrong_pin" msgid="4160978845968732624">"잘못된 PIN"</string> <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"잘못된 PIN입니다. 다시 시도해 주세요."</string> <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"또는 지문으로 잠금 해제하세요."</string> <string name="kg_fp_not_recognized" msgid="5183108260932029241">"지문이 인식되지 않았습니다."</string> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index 6fab1908fb2f..f32d27ff52f2 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -45,9 +45,9 @@ <string name="keyguard_sim_locked_message" msgid="7095293254587575270">"SIM карта кулпуланган."</string> <string name="keyguard_sim_puk_locked_message" msgid="2503428315518592542">"SIM карта PUK менен кулпуланган."</string> <string name="keyguard_sim_unlock_progress_dialog_message" msgid="8489092646014631659">"SIM картанын кулпусу ачылууда…"</string> - <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN-коддун аймагы"</string> + <string name="keyguard_accessibility_pin_area" msgid="7403009340414014734">"PIN коддун аймагы"</string> <string name="keyguard_accessibility_password" msgid="3524161948484801450">"Түзмөктүн сырсөзү"</string> - <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM-картанын PIN-кодунун аймагы"</string> + <string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"SIM-картанын PIN кодунун аймагы"</string> <string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"SIM-картанын PUK-кодунун аймагы"</string> <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Өчүрүү"</string> <string name="disable_carrier_button_text" msgid="7153361131709275746">"eSIM-картаны өчүрүү"</string> @@ -58,7 +58,7 @@ <string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"Графклк ачкч тура эмс. Кайтлап крүңз."</string> <string name="kg_wrong_password" msgid="4143127991071670512">"Сырсөз туура эмес"</string> <string name="kg_wrong_password_try_again" msgid="6602878676125765920">"Сырсөз туура эмес. Кайтлап крүңз."</string> - <string name="kg_wrong_pin" msgid="4160978845968732624">"PIN-код туура эмес"</string> + <string name="kg_wrong_pin" msgid="4160978845968732624">"PIN код туура эмес"</string> <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"PIN кд тура эмс. Кайтлап крүңз."</string> <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"Болбосо манжа изи менен кулпусун ачыңыз"</string> <string name="kg_fp_not_recognized" msgid="5183108260932029241">"Манжа изи таанылган жок"</string> @@ -88,20 +88,20 @@ <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Туура эмес графикалык ачкыч менен өтө көп аракет"</string> <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Туура эмес сырсөз менен өтө көп аракет"</string> <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# секунддан кийин кайталаңыз.}other{# секунддан кийин кайталаңыз.}}"</string> - <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM-картанын PIN-кодун киргизиңиз."</string> - <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" SIM-картасынын PIN-кодун киргизиңиз."</string> + <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM-картанын PIN кодун киргизиңиз."</string> + <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" SIM-картасынын PIN кодун киргизиңиз."</string> <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Түзмөктү мобилдик кызматсыз колдонуу үчүн eSIM-картаны өчүрүңүз."</string> <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"SIM-карта азыр жарактан чыкты. Улантуу үчүн PUK-кодду киргизиңиз. Анын чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз."</string> <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Эми SIM-картанын \"<xliff:g id="CARRIER">%1$s</xliff:g>\" байланыш оператору өчүрүлдү. Улантуу үчүн PUK-кодду киргизиңиз. Анын чоо-жайын билүү үчүн байланыш операторуна кайрылыңыз."</string> - <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Сиз каалаган PIN-кодду териңиз"</string> - <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Сиз каалаган PIN-кодду ырастаңыз"</string> + <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Сиз каалаган PIN кодду териңиз"</string> + <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Сиз каалаган PIN кодду ырастаңыз"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM картанын кулпусу ачылууда…"</string> - <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4–8 сандан турган PIN-кодду териңиз."</string> + <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4–8 сандан турган PIN кодду териңиз."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK-код 8 же андан көп сандан турушу керек."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"PIN-кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секунддан кийин дагы аракет кылып көрүңүз."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"PIN кодуңузду <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секунддан кийин дагы аракет кылып көрүңүз."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Сырсөзүңүздү <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тердиңиз. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секунддан кийин дагы аракет кылып көрүңүз."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Түзмөктү ачуучу графикалык ачкычты <xliff:g id="NUMBER_0">%1$d</xliff:g> жолу туура эмес тарттыңыз. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секунддан кийин дагы аракет кылып көрүңүз."</string> - <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM-картанын PIN-коду туура эмес. Эми түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуңузга кайрылышыңыз керек."</string> + <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM-картанын PIN коду туура эмес. Эми түзмөктү бөгөттөн чыгаруу үчүн байланыш операторуңузга кайрылышыңыз керек."</string> <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{SIM-картанын PIN коду туура эмес киргизилди. # аракет калды. Болбосо, түзмөктү бөгөттөн чыгаруу үчүн операторуңузга кайрылышыңыз керек болот.}other{SIM-картанын PIN коду туура эмес киргизилди. # аракет калды. }}"</string> <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"SIM-карта жараксыз. Байланыш операторуңузга кайрылыңыз."</string> <string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{SIM-картанын PUK коду туура эмес киргизилди. SIM-картанын биротоло жарактан чыгаарына # аракет калды.}other{SIM-картанын PUK коду туура эмес киргизилди. SIM-картанын биротоло жарактан чыгаарына # аракет калды.}}"</string> diff --git a/packages/SystemUI/res-keyguard/values-mk/strings.xml b/packages/SystemUI/res-keyguard/values-mk/strings.xml index 99e35f9936b5..e39cc9547eb9 100644 --- a/packages/SystemUI/res-keyguard/values-mk/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mk/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Се отклучува SIM-картичката…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Внесете PIN што содржи 4 - 8 броеви."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK-кодот треба да содржи 8 или повеќе броеви."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Погрешно сте го напишале вашиот PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Внесовте погрешен PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно по <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Погрешно сте ја напишале вашата лозинка <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Погрешно сте ја нацртале вашата шема за отклучување <xliff:g id="NUMBER_0">%1$d</xliff:g> пати. \n\nОбидете се повторно за <xliff:g id="NUMBER_1">%2$d</xliff:g> секунди."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Погрешен PIN-код за SIM, сега мора да контактирате со вашиот оператор за да го отклучите уредот."</string> diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml index eefc4919420e..54def0c95583 100644 --- a/packages/SystemUI/res-keyguard/values-mn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM-н түгжээг тайлж байна…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4-8 тооноос бүтэх ПИН-г оруулна уу."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK код 8-с цөөнгүй тооноос бүтнэ."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Та ПИН кодоо <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу орууллаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Та ПИН кодоо <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу орууллаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундийн дараа дахин оролдоно уу."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Та нууц үгээ <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу орууллаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Та тайлах хээг <xliff:g id="NUMBER_0">%1$d</xliff:g> удаа буруу орууллаа. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> секундын дараа дахин оролдоно уу."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM-н ПИН кодыг буруу оруулсан тул та төхөөрөмжийнхөө түгжээг тайлахын тулд оператор компанитайгаа холбогдоно уу."</string> diff --git a/packages/SystemUI/res-keyguard/values-mr/strings.xml b/packages/SystemUI/res-keyguard/values-mr/strings.xml index 82245319ffc5..206bdf2b36b8 100644 --- a/packages/SystemUI/res-keyguard/values-mr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-mr/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"सिम अनलॉक करत आहे…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4 ते 8 अंकांचा पिन टाईप करा."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK कोड 8 अंकी किंवा त्यापेक्षा अधिकचा असावा."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"तुम्ही तुमचा PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"तुम्ही तुमचा पिन <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"तुम्ही तुमचा पासवर्ड <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा चुकीच्या पद्धतीने टाइप केला आहे. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"तुम्ही तुमचा अनलॉक पॅटर्न <xliff:g id="NUMBER_0">%1$d</xliff:g> वेळा अयोग्यरितीने काढला. \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"सिम पिन कोड चुकीचा आहे तुम्ही आता तुमचे डिव्हाइस अनलॉक करण्यासाठी तुमच्या वाहकाशी संपर्क साधावा."</string> diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml index d7cd73022a7e..40f16871c56f 100644 --- a/packages/SystemUI/res-keyguard/values-ne/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM कार्ड अनलक गरिँदै छ…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"४ देखि ८ वटा नम्बर भएको एउटा PIN टाइप गर्नुहोस्।"</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK कोड ८ वा सो भन्दा बढी नम्बरको हुनु पर्छ।"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो PIN प्रविष्ट गर्नुभएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत PIN टाइप गर्नुभएको छ। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक आफ्नो गलत पासवर्ड प्रविष्ट गर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि प्रयास गर्नुहोस्।"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"तपाईंले <xliff:g id="NUMBER_0">%1$d</xliff:g> पटक गलत तरिकाले आफ्नो अनलक प्याटर्न कोर्नुभएको छ। \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> सेकेन्डमा फेरि कोसिस गर्नुहोस्।"</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM को PIN कोड गलत छ। तपाईंले अब आफ्नो यन्त्र खोल्न आफ्नो सेवा प्रदायकलाई सम्पर्क गर्नै पर्ने हुन्छ।"</string> diff --git a/packages/SystemUI/res-keyguard/values-or/strings.xml b/packages/SystemUI/res-keyguard/values-or/strings.xml index 510ff7da1536..bcf59847afa5 100644 --- a/packages/SystemUI/res-keyguard/values-or/strings.xml +++ b/packages/SystemUI/res-keyguard/values-or/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIMକୁ ଅନଲକ କରାଯାଉଛି…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4 ରୁ 8 ନମ୍ବର ବିଶିଷ୍ଟ ଏକ PIN ଟାଇପ୍ କରନ୍ତୁ।"</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK କୋଡ୍ରେ 8ଟି କିମ୍ବା ଅଧିକ ନମ୍ବର ରହିଥାଏ।"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"ଆପଣଙ୍କ PIN ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g>ଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g>ଥର ଭୁଲ ଭାବେ ଆପଣଙ୍କ PIN ଟାଇପ କରିଛନ୍ତି। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"ଆପଣଙ୍କ ପାସ୍ୱର୍ଡକୁ ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g> ଥର ଭୁଲ ଭାବେ ଟାଇପ୍ କରିଛନ୍ତି। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"ଆପଣଙ୍କ ଲକ୍ ଖୋଲିବା ପାଟର୍ନକୁ ଆପଣ <xliff:g id="NUMBER_0">%1$d</xliff:g>ଥର ଭୁଲ ଭାବେ ଅଙ୍କନ କରିଛନ୍ତି। \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> ସେକେଣ୍ଡ ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"ଭୁଲ SIM PIN କୋଡ, ଆପଣଙ୍କ ଡିଭାଇସକୁ ଅନଲକ କରିବା ପାଇଁ ଏବେ ହିଁ ନିଜ କ୍ଯାରିଅରଙ୍କ ସହ କଣ୍ଟାକ୍ଟ କରନ୍ତୁ।"</string> diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml index 3be5b7ff984b..868bf2210d5e 100644 --- a/packages/SystemUI/res-keyguard/values-pl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml @@ -98,8 +98,8 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Odblokowuję kartę SIM…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Wpisz kod PIN o długości od 4 do 8 cyfr."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Kod PUK musi mieć co najmniej 8 cyfr."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> wpisałeś nieprawidłowy kod PIN. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> - <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> wpisałeś nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> został wpisany nieprawidłowy kod PIN. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> + <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> zostało wpisane nieprawidłowe hasło. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Po raz <xliff:g id="NUMBER_0">%1$d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. \n\nSpróbuj ponownie za <xliff:g id="NUMBER_1">%2$d</xliff:g> s."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Nieprawidłowy kod PIN karty SIM. Musisz teraz skontaktować się z operatorem, by odblokował Twoje urządzenie."</string> <string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # próbę, zanim trzeba będzie skontaktować się z operatorem, aby odblokował Twoje urządzenie.}few{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # próby. }many{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # prób. }other{Nieprawidłowy kod PIN karty SIM. Masz jeszcze # próby. }}"</string> diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 8ae6018a6221..e1a0c43202c4 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/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">"Introduza o PIN."</string> + <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Introduza o PIN"</string> <string name="keyguard_enter_pin" msgid="8114529922480276834">"Introduza o PIN"</string> <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Introduza o padrão"</string> <string name="keyguard_enter_pattern" msgid="7616595160901084119">"Desenhe o padrão"</string> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml index 2a8092010a37..3d1cb5ee6177 100644 --- a/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml +++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/integers.xml @@ -16,5 +16,5 @@ --> <resources> <!-- Invisibility to use for the date & weather view when it is disabled by a clock --> - <integer name="keyguard_date_weather_view_invisibility">8</integer> + <integer name="keyguard_date_weather_view_invisibility">4</integer> </resources> diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml index 439c836cd53d..f8bf307800cc 100644 --- a/packages/SystemUI/res-keyguard/values-te/strings.xml +++ b/packages/SystemUI/res-keyguard/values-te/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">"మీ పిన్ని నమోదు చేయండి"</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> @@ -58,7 +58,7 @@ <string name="kg_wrong_pattern_try_again" msgid="3603524940234151881">"ఆకృతి తప్పు. మళ్లీ గీయండి."</string> <string name="kg_wrong_password" msgid="4143127991071670512">"పాస్వర్డ్ తప్పు"</string> <string name="kg_wrong_password_try_again" msgid="6602878676125765920">"పాస్వర్డ్ తప్పు. రీట్రై."</string> - <string name="kg_wrong_pin" msgid="4160978845968732624">"పిన్ తప్పు"</string> + <string name="kg_wrong_pin" msgid="4160978845968732624">"PIN తప్పు"</string> <string name="kg_wrong_pin_try_again" msgid="3129729383303430190">"PIN తప్పు. రీట్రై చేయండి."</string> <string name="kg_wrong_input_try_fp_suggestion" msgid="3143861542242024833">"లేదా వేలిముద్రతో అన్లాక్ చేయండి"</string> <string name="kg_fp_not_recognized" msgid="5183108260932029241">"వేలిముద్ర గుర్తించబడలేదు"</string> @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIMను అన్లాక్ చేస్తోంది…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4 నుండి 8 సంఖ్యలు ఉండే పిన్ను టైప్ చేయండి."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK కోడ్ అనేది 8 లేదా అంతకంటే ఎక్కువ సంఖ్యలు ఉండాలి."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"మీరు మీ పిన్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ట్రై చేయండి."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"మీ PINను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్ల తర్వాత మళ్లీ ట్రై చేయండి."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"మీరు మీ పాస్వర్డ్ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ట్రై చేయండి."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"మీరు మీ అన్లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ట్రై చేయండి."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM పిన్ కోడ్ తప్పు, ఇప్పుడు మీ డివైజ్ను అన్లాక్ చేయాలంటే, మీరు తప్పనిసరిగా మీ క్యారియర్ను సంప్రదించాలి."</string> diff --git a/packages/SystemUI/res-keyguard/values-th/strings.xml b/packages/SystemUI/res-keyguard/values-th/strings.xml index e8c7ef9c33cc..4df30dc3be36 100644 --- a/packages/SystemUI/res-keyguard/values-th/strings.xml +++ b/packages/SystemUI/res-keyguard/values-th/strings.xml @@ -117,7 +117,7 @@ <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ใช้รหัสผ่านแทนเพื่อเพิ่มความปลอดภัย"</string> <string name="kg_prompt_reason_device_admin" msgid="6961159596224055685">"ผู้ดูแลระบบล็อกอุปกรณ์"</string> <string name="kg_prompt_reason_user_request" msgid="6015774877733717904">"มีการล็อกอุปกรณ์ด้วยตัวเอง"</string> - <string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จัก"</string> + <string name="kg_face_not_recognized" msgid="7903950626744419160">"ไม่รู้จักลายนิ้วมือ"</string> <string name="kg_face_sensor_privacy_enabled" msgid="939511161763558512">"เปิดการเข้าถึงกล้องในการตั้งค่าเพื่อใช้การปลดล็อกด้วยใบหน้า"</string> <string name="kg_password_default_pin_message" msgid="1434544655827987873">"{count,plural, =1{ป้อน PIN ของซิม คุณพยายามได้อีก # ครั้งก่อนที่จะต้องติดต่อผู้ให้บริการเพื่อปลดล็อกอุปกรณ์}other{ป้อน PIN ของซิม คุณลองได้อีก # ครั้ง}}"</string> <string name="kg_password_default_puk_message" msgid="1025139786449741950">"{count,plural, =1{ตอนนี้ซิมถูกปิดใช้แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ}other{ตอนนี้ซิมถูกปิดใช้แล้ว ป้อนรหัส PUK เพื่อดำเนินการต่อ คุณพยายามได้อีก # ครั้งก่อนที่ซิมจะใช้งานไม่ได้อย่างถาวร โปรดติดต่อสอบถามรายละเอียดจากผู้ให้บริการ}}"</string> diff --git a/packages/SystemUI/res-keyguard/values-tl/strings.xml b/packages/SystemUI/res-keyguard/values-tl/strings.xml index 4d9102c0fefd..70328866b4e8 100644 --- a/packages/SystemUI/res-keyguard/values-tl/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tl/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"Ina-unlock ang SIM…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Mag-type ng PIN na 4 hanggang 8 numero."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Dapat ay 8 numero o higit pa ang PUK code."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Na-type mo nang mali ang iyong PIN nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Na-type mo nang mali ang iyong PIN nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukan ulit pagkatapos ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%1$d</xliff:g> (na) beses. \n\nSubukang muli sa loob ng <xliff:g id="NUMBER_1">%2$d</xliff:g> (na) segundo."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Mali ang PIN code ng SIM, dapat ka nang makipag-ugnayan sa iyong carrier upang i-unlock ang iyong device."</string> diff --git a/packages/SystemUI/res-keyguard/values-tr/strings.xml b/packages/SystemUI/res-keyguard/values-tr/strings.xml index a2268ef6cfff..ff0bc9a867e6 100644 --- a/packages/SystemUI/res-keyguard/values-tr/strings.xml +++ b/packages/SystemUI/res-keyguard/values-tr/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM\'in kilidi açılıyor…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"4 ila 8 haneli bir PIN yazın."</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK kodu 8 veya daha çok basamaklı bir sayı olmalıdır."</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"PIN kodunuzu <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış girdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"PIN kodunuzu <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış girdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye sonra tekrar deneyin."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Şifrenizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış yazdınız. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%1$d</xliff:g> kez yanlış çizdiniz. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> saniye içinde tekrar deneyin."</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Yanlış SIM PIN kodu. Cihazınızın kilidini açmak için artık operatörünüzle bağlantı kurmanız gerekiyor."</string> diff --git a/packages/SystemUI/res-keyguard/values-ur/strings.xml b/packages/SystemUI/res-keyguard/values-ur/strings.xml index 596e47768444..dc6ef4dc9ac0 100644 --- a/packages/SystemUI/res-keyguard/values-ur/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ur/strings.xml @@ -98,7 +98,7 @@ <string name="kg_sim_unlock_progress_dialog_message" msgid="1123048780346295748">"SIM کو غیر مقفل کیا جا رہا ہے…"</string> <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"ایسا PIN ٹائپ کریں جو 4 تا 8 اعداد پر مشتمل ہو۔"</string> <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK کوڈ 8 یا زائد اعداد پر مشتمل ہونا چاہیے۔"</string> - <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"آپ نے اپنا PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string> + <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"آپ نے <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اپنا PIN غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"آپ نے اپنا پاس ورڈ <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ٹائپ کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"آپ نے اپنا غیر مقفل کرنے کا پیٹرن <xliff:g id="NUMBER_0">%1$d</xliff:g> بار غلط طریقے سے ڈرا کیا ہے۔ \n\n <xliff:g id="NUMBER_1">%2$d</xliff:g> سیکنڈ میں دوبارہ کوشش کریں۔"</string> <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"غلط SIM PIN کوڈ، اب آپ کو اپنا آلہ غیر مقفل کرنے کیلئے اپنے کیریئر سے رابطہ کرنا ہوگا۔"</string> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 4b7968953420..39dd90e5ed60 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -148,4 +148,9 @@ <dimen name="default_dot_diameter">34dp</dimen> <dimen name="default_dot_spacing">0dp</dimen> + <!-- Weather clock smartspace scaling to apply for the weather clock --> + <item name="weather_clock_smartspace_scale" type="dimen" format="float">1.0</item> + <dimen name="weather_clock_smartspace_translateX">0dp</dimen> + <dimen name="weather_clock_smartspace_translateY">0dp</dimen> + </resources> diff --git a/packages/SystemUI/res-keyguard/values/ids.xml b/packages/SystemUI/res-keyguard/values/ids.xml index 0dff4ffa3866..1435907eaef6 100644 --- a/packages/SystemUI/res-keyguard/values/ids.xml +++ b/packages/SystemUI/res-keyguard/values/ids.xml @@ -17,4 +17,18 @@ <resources> <item type="id" name="header_footer_views_added_tag_key" /> + + <!-- animation channels for keyguard status area --> + <item type="id" name="translate_x_clock_design_animator_tag" /> + <item type="id" name="translate_x_clock_design_animator_start_tag" /> + <item type="id" name="translate_x_clock_design_animator_end_tag" /> + <item type="id" name="translate_x_aod_animator_tag" /> + <item type="id" name="translate_x_aod_animator_start_tag" /> + <item type="id" name="translate_x_aod_animator_end_tag" /> + <item type="id" name="translate_y_clock_size_animator_tag" /> + <item type="id" name="translate_y_clock_size_animator_start_tag" /> + <item type="id" name="translate_y_clock_size_animator_end_tag" /> + <item type="id" name="translate_y_clock_design_animator_tag" /> + <item type="id" name="translate_y_clock_design_animator_start_tag" /> + <item type="id" name="translate_y_clock_design_animator_end_tag" /> </resources> diff --git a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml index a8f0cc3a1d92..4a9d41fae1d5 100644 --- a/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml +++ b/packages/SystemUI/res/drawable/stat_sys_ringer_silent.xml @@ -14,20 +14,6 @@ Copyright (C) 2015 The Android Open Source Project limitations under the License. --> <inset xmlns:android="http://schemas.android.com/apk/res/android" - android:insetLeft="3dp" - android:insetRight="3dp"> - <vector android:width="18dp" - android:height="18dp" - android:viewportWidth="24.0" - android:viewportHeight="24.0"> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4C10,21.1 10.9,22 12,22z"/> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M16,16L2.81,2.81L1.39,4.22l4.85,4.85C6.09,9.68 6,10.33 6,11v6H4v2h12.17l3.61,3.61l1.41,-1.41L16,16zM8,17c0,0 0.01,-6.11 0.01,-6.16L14.17,17H8z"/> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M12,6.5c2.49,0 4,2.02 4,4.5v2.17l2,2V11c0,-3.07 -1.63,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5S10.5,3.17 10.5,4v0.68C9.72,4.86 9.05,5.2 8.46,5.63L9.93,7.1C10.51,6.73 11.2,6.5 12,6.5z"/> - </vector> -</inset> + android:insetLeft="3dp" + android:insetRight="3dp" + android:drawable="@drawable/ic_speaker_mute" /> diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml index 81691898dfe5..efc661a6e974 100644 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml @@ -55,13 +55,7 @@ android:layout_height="wrap_content" android:layout_gravity="center"> - <com.airbnb.lottie.LottieAnimationView - android:id="@+id/biometric_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:contentDescription="@null" - android:scaleType="fitXY" /> + <include layout="@layout/auth_biometric_icon"/> <com.airbnb.lottie.LottieAnimationView android:id="@+id/biometric_icon_overlay" diff --git a/packages/SystemUI/res/layout/auth_biometric_icon.xml b/packages/SystemUI/res/layout/auth_biometric_icon.xml new file mode 100644 index 000000000000..b2df63dab700 --- /dev/null +++ b/packages/SystemUI/res/layout/auth_biometric_icon.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + + +<com.airbnb.lottie.LottieAnimationView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/biometric_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:contentDescription="@null" + android:scaleType="fitXY"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml index 054193a5b323..a595566ef817 100644 --- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml +++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml @@ -65,6 +65,8 @@ <TextView android:id="@+id/volume_value" android:animateLayoutChanges="true" + android:focusable="false" + android:importantForAccessibility="no" android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -75,6 +77,8 @@ <TextView android:id="@+id/title" + android:focusable="false" + android:importantForAccessibility="no" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|start" diff --git a/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json b/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json new file mode 100644 index 000000000000..b1d6a270bc67 --- /dev/null +++ b/packages/SystemUI/res/raw/fingerprint_dialogue_unlocked_to_checkmark_success_lottie.json @@ -0,0 +1 @@ +{"v":"5.7.13","fr":60,"ip":0,"op":55,"w":80,"h":80,"nm":"unlocked_to_checkmark_success","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[47.143,32,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[2.761,0],[0,-2.7],[0,0]],"o":[[0,0],[0,-2.7],[-2.761,0],[0,0],[0,0]],"v":[[5,5],[5,-0.111],[0,-5],[-5,-0.111],[-5.01,4]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[38,45,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[18,16],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".colorAccentPrimary","cl":"colorAccentPrimary","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[37.999,44.999,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-1.42,0],[0,1.42],[1.42,0],[0,-1.42]],"o":[[1.42,0],[0,-1.42],[-1.42,0],[0,1.42]],"v":[[0,2.571],[2.571,0],[0,-2.571],[-2.571,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.827450990677,0.890196084976,0.992156863213,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"t":10,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":85,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.5,40.75,0],"ix":2,"l":2},"a":{"a":0,"k":[12.5,-6.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[60,60,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":19,"s":[112,112,100]},{"t":30,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-10.556,-9.889],[7.444,6.555],[34.597,-20.486]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":4,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":10,"op":910,"st":10,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".green200","cl":"green200","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[93.5,93.5,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.658823529412,0.854901960784,0.709803921569,1],"ix":3},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[0]},{"t":15,"s":[100]}],"ix":4},"w":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[0]},{"t":20,"s":[4]}],"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":10,"op":77,"st":10,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey700","cl":"grey700","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40,40,0],"ix":2,"l":2},"a":{"a":0,"k":[40.25,40.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[22.12,0],[0,-22.08],[-22.08,0],[0,22.08]],"o":[[-22.08,0],[0,22.08],[22.12,0],[0,-22.08]],"v":[[-0.04,-40],[-40,0],[-0.04,40],[40,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.278431385756,0.278431385756,0.278431385756,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"t":20,"s":[0]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[40.25,40.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index f7f45e22b74d..cd56b8f1135a 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Gesig is gestaaf"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bevestig"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tik op Bevestig om te voltooi"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ontsluit met gesig. Druk om voort te gaan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Gesig is herken. Druk om voort te gaan."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan nie stoor nie. Probeer weer."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan nie stoor nie."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gebruik minstens 4 karakters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gebruik minder as 16 karakters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string> <string name="basic_status" msgid="2315371112182658176">"Maak gesprek oop"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 17f7d5a0663d..e754e25f1e38 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"መልክ ተረጋግጧል"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ተረጋግጧል"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ለማጠናቀቅ አረጋግጥን መታ ያድርጉ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"በመልክ ተከፍቷል። ለመቀጠል ይጫኑ።"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"መልክ ተለይቶ ታውቋል። ለመቀጠል ይጫኑ።"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ማስቀመጥ አልተቻለም። እንደገና ይሞክሩ።"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ማስቀመጥ አልተቻለም።"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ቢያንስ 4 ቁምፊዎችን ይጠቀሙ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ከ16 የሚያንሱ ቁምፊዎችን ይጠቀሙ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"የግንብ ቁጥር"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"የገንባ ቁጥር ወደ ቅንጥብ ሰሌዳ ተቀድቷል።"</string> <string name="basic_status" msgid="2315371112182658176">"ውይይት ይክፈቱ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 71266fb043f2..d378aa8a8913 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"تمّت مصادقة الوجه."</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تمّ التأكيد."</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"يمكنك النقر على \"تأكيد\" لإكمال المهمة."</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"تم فتح قفل جهازك عند تقريبه من وجهك. اضغط للمتابعة."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"تم التعرّف على الوجه. اضغط للمتابعة."</string> @@ -281,7 +281,7 @@ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"جارٍ التفعيل…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"توفير البيانات مفعّل"</string> <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{جهاز واحد}zero{# جهاز}two{جهازان}few{# أجهزة}many{# جهازًا}other{# جهاز}}"</string> - <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"الفلاش"</string> + <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"ضوء الفلاش"</string> <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"الكاميرا قيد الاستخدام"</string> <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"بيانات الجوّال"</string> <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"استخدام البيانات"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"لا يمكن إجراء الحفظ. يُرجى إعادة المحاولة."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"لا يمكن إجراء الحفظ."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"يجب استخدام 4 أحرف على الأقل."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"يجب أن يحتوي الرمز على أقل من 16 حرفًا."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string> <string name="basic_status" msgid="2315371112182658176">"محادثة مفتوحة"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 02637642b0fb..5f176ae64ab8 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"নিশ্চিত কৰিলে"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"সম্পূৰ্ণ কৰিবলৈ নিশ্চিত কৰক-ত টিপক"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা হৈছে। অব্যাহত ৰাখিবলৈ টিপক।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"মুখাৱয়ব চিনাক্ত কৰা হৈছে। অব্যাহত ৰাখিবলৈ টিপক।"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ছেভ কৰিব নোৱাৰি। পুনৰ চেষ্টা কৰক।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ছেভ কৰিব নোৱাৰি।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"অতি কমেও ৪ টা বৰ্ণ ব্যৱহাৰ কৰক"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"১৬ টাতকৈ কম বৰ্ণ ব্যৱহাৰ কৰক"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ডৰ নম্বৰ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ক্লিপব’ৰ্ডলৈ বিল্ডৰ নম্বৰ প্ৰতিলিপি কৰা হ’ল।"</string> <string name="basic_status" msgid="2315371112182658176">"বাৰ্তালাপ খোলক"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 047323fc662a..fcd433aed53f 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Üz doğrulandı"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Təsdiqləndi"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tamamlamaq üçün \"Təsdiq edin\" seçiminə toxunun"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Üz ilə kiliddən çıxarılıb. Davam etmək üçün basın."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Üz tanınıb. Davam etmək üçün basın."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Yadda saxlamaq mümkün deyil. Yenə cəhd edin."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Yadda saxlamaq mümkün deyil."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Ən azı 4 simvoldan istifadə edin"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Maksimum 16 simvoldan istifadə edin"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Montaj nömrəsi"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string> <string name="basic_status" msgid="2315371112182658176">"Açıq söhbət"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 5575274d1261..d37c709c9cee 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Lice je potvrđeno"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrđeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Dodirnite Potvrdi da biste završili"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Otključano je licem. Pritisnite da biste nastavili."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Lice je prepoznato. Pritisnite da biste nastavili."</string> @@ -210,7 +210,7 @@ <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Prozor sa obaveštenjima."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Brza podešavanja."</string> <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Brza podešavanja i traka sa obaveštenjima."</string> - <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključan ekran."</string> + <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključan ekran"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan ekran za posao"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvori"</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"potpuna tišina"</string> @@ -785,7 +785,7 @@ <string name="tuner_right" msgid="8247571132790812149">"Strelica udesno"</string> <string name="tuner_menu" msgid="363690665924769420">"Meni"</string> <string name="tuner_app" msgid="6949280415826686972">"Aplikacija <xliff:g id="APP">%1$s</xliff:g>"</string> - <string name="notification_channel_alerts" msgid="3385787053375150046">"Obaveštenja"</string> + <string name="notification_channel_alerts" msgid="3385787053375150046">"Upozorenja"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimci ekrana"</string> <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Čuvanje nije uspelo. Probajte ponovo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Čuvanje nije uspelo."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Koristite bar 4 znaka"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Koristite manje od 16 znakova"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string> <string name="basic_status" msgid="2315371112182658176">"Otvorite konverzaciju"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 1576fb56584d..518c5e662a21 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Твар распазнаны"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Пацверджана"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Націсніце \"Пацвердзіць\", каб завяршыць"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Разблакіравана распазнаваннем твару. Націсніце для працягу."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Твар распазнаны. Націсніце для працягу."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не ўдалося захаваць. Паўтарыце спробу."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не ўдалося захаваць."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Скарыстайце не менш як 4 сімвалы"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Скарыстайце менш за 16 сімвалаў"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Нумар зборкі"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Нумар зборкі скапіраваны ў буфер абмену."</string> <string name="basic_status" msgid="2315371112182658176">"Адкрытая размова"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index ec852345541c..96646ada1ef2 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лицето е удостоверено"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Потвърдено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Докоснете „Потвърждаване“ за завършване"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Отключено с лице. Натиснете, за да продължите."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лицето бе разпознато. Натиснете, за да продължите."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не може да се запази. Опитайте отново."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не може да се запази."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Използвайте поне 4 знака"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Използвайте по-малко от 16 знака"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string> <string name="basic_status" msgid="2315371112182658176">"Отворен разговор"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 3335c3d78b47..a53baad686a8 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ফেস যাচাই করা হয়েছে"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"কনফার্ম করা হয়েছে"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"সম্পূর্ণ করতে \'কনফার্ম করুন\' বোতামে ট্যাপ করুন"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ফেসের সাহায্যে আনলক করা হয়েছে। চালিয়ে যেতে প্রেস করুন।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ফেস শনাক্ত করা হয়েছে। চালিয়ে যেতে প্রেস করুন।"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"সেভ করা যাচ্ছে না। আবার চেষ্টা করুন।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"সেভ করা যাচ্ছে না।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"কমপক্ষে ৪টি অক্ষর ব্যবহার করুন"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"১৬টির চেয়ে কম অক্ষর ব্যবহার করুন"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ড নম্বর"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"বিল্ড নম্বর ক্লিপবোর্ডে কপি করা হয়েছে।"</string> <string name="basic_status" msgid="2315371112182658176">"খোলা কথোপকথন"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 4d6d78c43428..00790b38beca 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Lice je provjereno"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrđeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Dodirnite Potvrdi da završite"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Otključano licem. Pritisnite da nastavite."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Lice prepoznato. Pritisnite da nastavite."</string> @@ -906,7 +906,7 @@ <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"uklonite iz omiljenog"</string> <string name="accessibility_control_move" msgid="8980344493796647792">"Premjesti na poziciju <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Kontrole"</string> - <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Odaberite kontrole uređaja da pristupite brzo"</string> + <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Odaberite kontrole uređaja za brzi pristup"</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Držite i prevucite da preuredite kontrole"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"Sve kontrole su uklonjene"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Promjene nisu sačuvane"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nije moguće sačuvati. Pokušajte ponovo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nije moguće sačuvati."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Koristite najmanje 4 znaka"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Koristite manje od 16 znakova"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string> <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a9984364bcb9..74b557b624e5 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Cara autenticada"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmat"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toca Confirma per completar"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S\'ha desbloquejat amb la cara. Prem per continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"S\'ha reconegut la cara. Prem per continuar."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No es pot desar. Torna-ho a provar."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No es pot desar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utilitza 4 caràcters com a mínim"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utilitza menys de 16 caràcters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa oberta"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 4530fb741cb4..97bf39f19223 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Obličej byl ověřen"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrzeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ověření dokončíte klepnutím na Potvrdit"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odemknuto obličejem. Pokračujte stisknutím."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Obličej rozpoznán. Pokračujte stisknutím."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Uložení se nezdařilo. Zkuste to znovu."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Uložení se nezdařilo."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Heslo musí mít alespoň 4 znaky"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Použijte méně než 16 znaků"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string> <string name="basic_status" msgid="2315371112182658176">"Otevřít konverzaci"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 38eda88b5286..7388fbf6d8a8 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansigtet er godkendt"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekræftet"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tryk på Bekræft for at udføre"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Låst op ved hjælp af ansigt. Tryk for at fortsætte."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansigt genkendt. Tryk for at fortsætte."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Der kan ikke gemmes. Prøv igen."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Der kan ikke gemmes."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Angiv mindst 4 tegn"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Angiv højst 16 tegn"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string> <string name="basic_status" msgid="2315371112182658176">"Åben samtale"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index b6b896f3957c..7608509fdbbd 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Gesicht authentifiziert"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bestätigt"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Zum Abschließen auf \"Bestätigen\" tippen"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Gerät mit dem Gesicht entsperrt. Tippe, um fortzufahren."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Gesicht erkannt. Tippe, um fortzufahren."</string> @@ -244,7 +244,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Eingabe"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hörhilfen"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hörgeräte"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Wird aktiviert…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string> @@ -468,7 +468,7 @@ <string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"Deine privaten Apps sind über <xliff:g id="VPN_APP">%1$s</xliff:g> mit dem Internet verbunden. Deine Netzwerkaktivitäten, einschließlich E-Mails und Browserdaten, sind für deinen VPN-Anbieter sichtbar."</string> <string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string> <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN-Einstellungen öffnen"</string> - <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dieses Gerät wird von deinen Eltern verwaltet. Sie können unter anderem Informationen über deine genutzten Apps, deinen Standort und deine Gerätenutzungsdauer einsehen und verwalten."</string> + <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dieses Gerät wird von deinen Eltern verwaltet. Sie können unter anderem Informationen über deine genutzten Apps, deinen Standort und deine Bildschirmzeit einsehen und verwalten."</string> <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string> <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Durch TrustAgent entsperrt"</string> <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> @@ -897,8 +897,8 @@ <string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{# Steuerelement hinzugefügt.}other{# Steuerelemente hinzugefügt.}}"</string> <string name="controls_removed" msgid="3731789252222856959">"Entfernt"</string> <string name="controls_panel_authorization_title" msgid="267429338785864842">"<xliff:g id="APPNAME">%s</xliff:g> hinzufügen?"</string> - <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> darf auswählen, welche Einstellungen und Inhalte hier angezeigt werden."</string> - <string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"Einstellungen für <xliff:g id="APPNAME">%s</xliff:g> entfernen?"</string> + <string name="controls_panel_authorization" msgid="7045551688535104194">"<xliff:g id="APPNAME">%s</xliff:g> darf auswählen, welche Steuerelemente und Inhalte hier angezeigt werden."</string> + <string name="controls_panel_remove_app_authorization" msgid="5920442084735364674">"Steuerelemente für <xliff:g id="APPNAME">%s</xliff:g> entfernen?"</string> <string name="accessibility_control_favorite" msgid="8694362691985545985">"Zu Favoriten hinzugefügt"</string> <string name="accessibility_control_favorite_position" msgid="54220258048929221">"Zu Favoriten hinzugefügt, Position <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="accessibility_control_not_favorite" msgid="1291760269563092359">"Aus Favoriten entfernt"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Speichern nicht möglich. Versuche es noch einmal."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Speichern nicht möglich."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gib mindestens vier Zeichen ein"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Maximal 16 Zeichen möglich"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string> <string name="basic_status" msgid="2315371112182658176">"Offene Unterhaltung"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 073e9f4be113..e4187543b335 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Έγινε έλεγχος ταυτότητας προσώπου"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Επιβεβαιώθηκε"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Πατήστε Επιβεβαίωση για ολοκλήρωση"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ξεκλείδωμα με αναγνώριση προσώπου. Πατήστε για συνέχεια."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Το πρόσωπο αναγνωρίστηκε. Πατήστε για συνέχεια."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Δεν είναι δυνατή η αποθήκευση. Δοκιμάστε ξανά."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Δεν είναι δυνατή η αποθήκευση."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Χρησιμοποιήστε τουλάχιστον 4 χαρακτήρες"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Χρησιμοποιήστε λιγότερους από 16 χαρακτήρες"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string> <string name="basic_status" msgid="2315371112182658176">"Άνοιγμα συνομιλίας"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 61c15e2cff7c..524864a1bc53 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognised. Press to continue."</string> @@ -244,7 +244,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing Aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -988,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index d442f6a47f7d..d8971ba0d30d 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -142,8 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> - <skip /> + <string name="biometric_dialog_tap_confirm_with_face" msgid="2378151312221818694">"Unlocked by face"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognized. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognized. Press the unlock icon to continue."</string> @@ -1004,7 +1003,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least 4 characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 61c15e2cff7c..524864a1bc53 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognised. Press to continue."</string> @@ -244,7 +244,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing Aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -988,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 61c15e2cff7c..524864a1bc53 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognised. Press to continue."</string> @@ -244,7 +244,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing Aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -988,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Available devices for audio output."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers & displays"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Speakers and displays"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Suggested devices"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Stop your shared session to move media to another device"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Stop"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least four characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index cbc9538958cc..368632b12250 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -142,8 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Face authenticated"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmed"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tap Confirm to complete"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> - <skip /> + <string name="biometric_dialog_tap_confirm_with_face" msgid="2378151312221818694">"Unlocked by face"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Unlocked by face. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Face recognized. Press to continue."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Face recognized. Press the unlock icon to continue."</string> @@ -1004,7 +1003,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Can’t save. Try again."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Can’t save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use at least 4 characters"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use fewer than 16 characters"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build number"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Open conversation"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 485595974f0c..06647b79ce78 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Se autenticó el rostro"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmado"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Presiona Confirmar para completar"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueo con rostro. Presiona para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rostro reconocido. Presiona para continuar."</string> @@ -270,7 +270,7 @@ <string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillo"</string> <string name="quick_settings_inversion_label" msgid="3501527749494755688">"Invertir colores"</string> <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corregir colores"</string> - <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Tamaño de la fuente"</string> + <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"Tamaño de fuente"</string> <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"Administrar usuarios"</string> <string name="quick_settings_done" msgid="2163641301648855793">"Listo"</string> <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Cerrar"</string> @@ -906,7 +906,7 @@ <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"quitar de favoritos"</string> <string name="accessibility_control_move" msgid="8980344493796647792">"Mover a la posición <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Controles"</string> - <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Elige los controles de dispositivos para acceso rápido"</string> + <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Elige los controles de dispositivos a los que quieres acceder rápido"</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mantén presionado y arrastra un control para reubicarlo"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"Se quitaron todos los controles"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"No se guardaron los cambios"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No se puede guardar. Vuelve a intentarlo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No se puede guardar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Usa al menos 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Usa menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string> <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 23a2d1354520..c6b8d5081fae 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Cara autenticada"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toca Confirmar para completar la acción"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado con la cara. Pulsa para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Cara reconocida. Pulsa para continuar."</string> @@ -989,7 +989,7 @@ <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volumen"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g> %%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Altavoces y pantallas"</string> - <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Sugerencias de dispositivos"</string> + <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Detén tu sesión compartida para transferir el contenido multimedia a otro dispositivo"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Detener"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Cómo funciona la emisión"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"No se puede guardar. Inténtalo de nuevo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"No se puede guardar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Usa 4 caracteres como mínimo"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Usa menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string> <string name="basic_status" msgid="2315371112182658176">"Conversación abierta"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 2328b7be7d85..bb9198eeeaf6 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Nägu on autenditud"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Kinnitatud"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Lõpuleviimiseks puudutage nuppu Kinnita"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Avati näoga. Vajutage jätkamiseks."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Nägu tuvastati. Vajutage jätkamiseks."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ei saa salvestada. Proovige uuesti."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ei saa salvestada."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Kasutage vähemalt 4 tähemärki"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Kasutage vähem kui 16 tähemärki"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string> <string name="basic_status" msgid="2315371112182658176">"Avage vestlus"</string> @@ -1145,7 +1146,7 @@ <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string> <string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string> <string name="call_from_work_profile_title" msgid="5418253516453177114">"Isiklikust rakendusest ei saa helistada"</string> - <string name="call_from_work_profile_text" msgid="2856337395968118274">"Teie organisatsioon lubab helistada ainult töörakendustest"</string> + <string name="call_from_work_profile_text" msgid="2856337395968118274">"Teie organisatsioon lubab helistada ainult töörakendustest."</string> <string name="call_from_work_profile_action" msgid="2937701298133010724">"Lülitu tööprofiilile"</string> <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"• Installige töö telefonirakendus"</string> <string name="call_from_work_profile_close" msgid="5830072964434474143">"Tühista"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 269df8ad9278..a24dfef0cf1b 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Autentifikatu da aurpegia"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Berretsita"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Amaitzeko, sakatu \"Berretsi\""</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Aurpegiaren bidez desblokeatu da. Sakatu aurrera egiteko."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ezagutu da aurpegia. Sakatu aurrera egiteko."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ezin da gorde. Saiatu berriro."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ezin da gorde."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Erabili lau karaktere gutxienez"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Erabili 16 karaktere baino gutxiago"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string> <string name="basic_status" msgid="2315371112182658176">"Elkarrizketa irekia"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 1e0189c8fcfd..1b029051fa00 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -69,7 +69,7 @@ <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"فعال کردن USB"</string> <string name="learn_more" msgid="4690632085667273811">"بیشتر بدانید"</string> <string name="global_action_screenshot" msgid="2760267567509131654">"نماگرفت"</string> - <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Extend Unlock غیرفعال شده است"</string> + <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"«تمدید حالت باز» غیرفعال شده است"</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"تصویری ارسال کرد"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"درحال ذخیره نماگرفت…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"درحال ذخیره کردن نماگرفت در نمایه کاری…"</string> @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالتسنجی شد"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"قفلْ با چهره باز شد. برای ادامه، فشار دهید."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"چهره شناسایی شد. برای ادامه، فشار دهید."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ذخیره نشد. دوباره امتحان کنید."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ذخیره نشد."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"حداقل از ۴ نویسه استفاده کنید"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"از کمتر از ۱۶ نویسه استفاده کنید"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریدهدان کپی شد."</string> <string name="basic_status" msgid="2315371112182658176">"باز کردن مکالمه"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 2878c66ce5b3..91b074d1396f 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Kasvot tunnistettu"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Vahvistettu"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Valitse lopuksi Vahvista"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Avattu kasvojen avulla. Jatka painamalla."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Kasvot tunnistettu. Jatka painamalla."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tallennus ei onnistu. Yritä uudelleen."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tallennus ei onnistu."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Käytä vähintään 4 merkkiä"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Käytä alle 16 merkkiä"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string> <string name="basic_status" msgid="2315371112182658176">"Avaa keskustelu"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 0e7a0bd28119..7d2d92abf954 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -96,25 +96,19 @@ <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> a détecté cette capture d\'écran."</string> <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> et d\'autres applications ouvertes ont détecté cette capture d\'écran."</string> <string name="app_clips_save_add_to_note" msgid="3460200751278069445">"Ajouter à une note"</string> - <!-- no translation found for screenrecord_title (4257171601439507792) --> - <skip /> + <string name="screenrecord_title" msgid="4257171601439507792">"Enregistreur d\'écran"</string> <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Trait. de l\'enregist. d\'écran…"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notification en cours pour une session d\'enregistrement d\'écran"</string> - <!-- no translation found for screenrecord_permission_dialog_title (303380743267672953) --> - <skip /> - <!-- no translation found for screenrecord_permission_dialog_warning_entire_screen (4152602778470789965) --> - <skip /> - <!-- no translation found for screenrecord_permission_dialog_warning_single_app (6818309727772146138) --> - <skip /> - <!-- no translation found for screenrecord_permission_dialog_continue (5811122652514424967) --> - <skip /> + <string name="screenrecord_permission_dialog_title" msgid="303380743267672953">"Commencer l\'enregistrement?"</string> + <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="4152602778470789965">"Pendant l\'enregistrement, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="screenrecord_permission_dialog_warning_single_app" msgid="6818309727772146138">"Lorsque vous enregistrez une application, Android a accès à tout ce qui est visible ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="screenrecord_permission_dialog_continue" msgid="5811122652514424967">"Commencer l\'enregistrement"</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Enregistrer des fichiers audio"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Audio de l\'appareil"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons de l\'appareil comme la musique, les appels et les sonneries"</string> <string name="screenrecord_mic_label" msgid="2111264835791332350">"Microphone"</string> <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Audio de l\'appareil et du microphone"</string> - <!-- no translation found for screenrecord_continue (4055347133700593164) --> - <skip /> + <string name="screenrecord_continue" msgid="4055347133700593164">"Commencer"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Enregistrement de l\'écran"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Enregistrement de l\'écran et de l\'audio"</string> <string name="screenrecord_taps_label" msgid="1595690528298857649">"Afficher les endroits touchés à l\'écran"</string> @@ -148,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Visage authentifié"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmé"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Touchez Confirmer pour terminer"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Déverr. par reconnaissance faciale. Appuyez pour continuer."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Visage reconnu. Appuyez pour continuer."</string> @@ -401,42 +395,24 @@ <string name="user_remove_user_message" msgid="6702834122128031833">"Toutes les applications et les données de cet utilisateur seront supprimées."</string> <string name="user_remove_user_remove" msgid="8387386066949061256">"Supprimer"</string> <string name="media_projection_dialog_title" msgid="3316063622495360646">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> - <!-- no translation found for media_projection_dialog_warning (1303664408388363598) --> - <skip /> - <!-- no translation found for media_projection_sys_service_dialog_title (3751133258891897878) --> - <skip /> - <!-- no translation found for media_projection_sys_service_dialog_warning (2443872865267330320) --> - <skip /> - <!-- no translation found for screen_share_permission_dialog_option_entire_screen (3131200488455089620) --> - <skip /> - <!-- no translation found for screen_share_permission_dialog_option_single_app (4350961814397220929) --> - <skip /> - <!-- no translation found for screen_share_permission_app_selector_title (1404878013670347899) --> - <skip /> - <!-- no translation found for media_projection_entry_app_permission_dialog_title (9155535851866407199) --> - <skip /> - <!-- no translation found for media_projection_entry_app_permission_dialog_warning_entire_screen (8736391633234144237) --> - <skip /> - <!-- no translation found for media_projection_entry_app_permission_dialog_warning_single_app (5211695779082563959) --> - <skip /> - <!-- no translation found for media_projection_entry_app_permission_dialog_continue (295463518195075840) --> - <skip /> - <!-- no translation found for media_projection_entry_cast_permission_dialog_title (8860150223172993547) --> - <skip /> - <!-- no translation found for media_projection_entry_cast_permission_dialog_warning_entire_screen (1986212276016817231) --> - <skip /> - <!-- no translation found for media_projection_entry_cast_permission_dialog_warning_single_app (9900961380294292) --> - <skip /> - <!-- no translation found for media_projection_entry_cast_permission_dialog_continue (7209890669948870042) --> - <skip /> - <!-- no translation found for media_projection_entry_generic_permission_dialog_title (4519802931547483628) --> - <skip /> - <!-- no translation found for media_projection_entry_generic_permission_dialog_warning_entire_screen (5407906851409410209) --> - <skip /> - <!-- no translation found for media_projection_entry_generic_permission_dialog_warning_single_app (3454859977888159495) --> - <skip /> - <!-- no translation found for media_projection_entry_generic_permission_dialog_continue (8640381403048097116) --> - <skip /> + <string name="media_projection_dialog_warning" msgid="1303664408388363598">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information qui est visible sur votre écran ou lue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et les contenus audio que vous faites jouer."</string> + <string name="media_projection_sys_service_dialog_title" msgid="3751133258891897878">"Commencer à enregistrer ou à diffuser?"</string> + <string name="media_projection_sys_service_dialog_warning" msgid="2443872865267330320">"Le service offrant cette fonction aura accès à toute l\'information qui est visible sur votre écran ou lu sur votre appareil pendant que vous enregistrez ou diffusez. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et le contenu audio que vous faites jouer."</string> + <string name="screen_share_permission_dialog_option_entire_screen" msgid="3131200488455089620">"Écran entier"</string> + <string name="screen_share_permission_dialog_option_single_app" msgid="4350961814397220929">"Une seule application"</string> + <string name="screen_share_permission_app_selector_title" msgid="1404878013670347899">"Partager ou enregistrer une application"</string> + <string name="media_projection_entry_app_permission_dialog_title" msgid="9155535851866407199">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string> + <string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Lorsque vous partagez, enregistrez ou diffusez, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lorsque vous partagez, enregistrez ou diffusez une application, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Commencer"</string> + <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Commencer la diffusion?"</string> + <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Lorsque vous diffusez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lorsque vous diffusez une application, Android a accès à tout ce qui est visible sur votre écran ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Commencer la diffusion"</string> + <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Commencer à partager?"</string> + <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"Lorsque vous partagez, enregistrez ou diffusez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"Lorsque vous partagez, enregistrez ou diffusez une application, Android a accès à tout ce qui est visible sur votre écran ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string> + <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"Commencer"</string> <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Bloquée par votre administrateur informatique"</string> <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"La fonctionnalité de capture d\'écran est désactivée par l\'application Device Policy"</string> <string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string> @@ -1028,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossible d\'enregistrer. Réessayez."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossible d\'enregistrer."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utilisez au moins 4 caractères"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utilisez moins de 16 caractères"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string> <string name="basic_status" msgid="2315371112182658176">"Ouvrir la conversation"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 21176b32834a..496ae69342d5 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Visage authentifié"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmé"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Appuyez sur \"Confirmer\" pour terminer"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Déverrouillé par visage. Appuyez pour continuer."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Visage reconnu. Appuyez pour continuer."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossible d\'enregistrer. Réessayez."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossible d\'enregistrer."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utilisez au moins quatre caractères"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utilisez moins de 16 caractères"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string> <string name="basic_status" msgid="2315371112182658176">"Conversation ouverte"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index fc3d03320ee1..bca8d5173deb 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Autenticouse a cara"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toca Confirmar para completar o proceso"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Usouse o desbloqueo facial. Preme para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Recoñeceuse a cara. Preme para continuar."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Non se puido gardar a información. Téntao de novo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Non se pode gardar a información."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Utiliza como mínimo 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Utiliza menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index c5b3d2e4209c..8f86401574e0 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ચહેરાનું પ્રમાણીકરણ થયું"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"પુષ્ટિ કરી"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"પરીક્ષણ પૂર્ણ કરવા કન્ફર્મ કરોને ટૅપ કરો"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ચહેરા દ્વારા અનલૉક કર્યું. આગળ વધવા માટે દબાવો."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ચહેરો ઓળખ્યો. આગળ વધવા માટે દબાવો."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"સાચવી શકતા નથી. ફરી પ્રયાસ કરો."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"સાચવી શકતા નથી."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ઓછામાં ઓછા 4 અક્ષરનો ઉપયોગ કરો"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 કરતાં ઓછા અક્ષરનો ઉપયોગ કરો"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"બિલ્ડ નંબર"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"બિલ્ડ નંબર ક્લિપબૉર્ડ પર કૉપિ કર્યો."</string> <string name="basic_status" msgid="2315371112182658176">"વાતચીત ખોલો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index fb4de7046d1a..b2d4b883f359 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"चेहरे की पुष्टि हो गई"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"पुष्टि हो गई"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"\'पुष्टि करें\' पर टैप करके पूरा करें"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"चेहरे से अनलॉक किया गया. जारी रखने के लिए टैप करें."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"चेहरे की पहचान हो गई. जारी रखने के लिए टैप करें."</string> @@ -290,7 +290,7 @@ <string name="quick_settings_cellular_detail_data_used" msgid="6798849610647988987">"<xliff:g id="DATA_USED">%s</xliff:g> उपयोग किया गया"</string> <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string> <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string> - <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन"</string> + <string name="quick_settings_work_mode_label" msgid="6440531507319809121">"वर्क ऐप्लिकेशन"</string> <string name="quick_settings_night_display_label" msgid="8180030659141778180">"नाइट लाइट"</string> <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"शाम को चालू की जाएगी"</string> <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सुबह तक चालू रहेगी"</string> @@ -912,7 +912,7 @@ <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"बदलाव सेव नहीं किए गए"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"दूसरे ऐप्लिकेशन देखें"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"फिर से व्यवस्थित करें"</string> - <string name="controls_favorite_add_controls" msgid="1221420435546694004">"कंट्रोल बटन जोड़ें"</string> + <string name="controls_favorite_add_controls" msgid="1221420435546694004">"कंट्रोल जोड़ें"</string> <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"बदलाव करने के लिए वापस जाएं"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"कंट्रोल लोड नहीं किए जा सके. <xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन देखें, ताकि यह पक्का किया जा सके कि ऐप्लिकेशन की सेटिंग में कोई बदलाव नहीं हुआ है."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"इस सेटिंग के साथ काम करने वाले कंट्रोल उपलब्ध नहीं हैं"</string> @@ -984,7 +984,7 @@ <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string> - <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्टिंग करना रोकें"</string> + <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string> <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ऑडियो आउटपुट के लिए उपलब्ध डिवाइस."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"वॉल्यूम"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"सेव नहीं किया जा सका. फिर से कोशिश करें."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"सेव नहीं किया जा सका."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"कम से कम चार वर्ण इस्तेमाल करें"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 वर्ण से कम इस्तेमाल करें"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string> <string name="basic_status" msgid="2315371112182658176">"ऐसी बातचीत जिसमें इंटरैक्शन डेटा मौजूद नहीं है"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 9cb3fe866be3..54d87f282d11 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Lice je autentificirano"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrđeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Dodirnite Potvrdi za dovršetak"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Otključano pomoću lica. Pritisnite da biste nastavili."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Lice je prepoznato. Pritisnite da biste nastavili."</string> @@ -211,7 +211,7 @@ <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Brze postavke."</string> <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Brze postavke i zaslon obavijesti."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Zaključavanje zaslona."</string> - <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključan zaslon radnog profila"</string> + <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Zaključani zaslon radnog profila"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Zatvaranje"</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"potpuna tišina"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"samo alarmi"</string> @@ -759,7 +759,7 @@ <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Uređivanje redoslijeda postavki."</string> <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Izbornik tipke za uključivanje/isključivanje"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string> - <string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključan zaslon"</string> + <string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključani zaslon"</string> <string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefon se isključio zbog vrućine"</string> <string name="thermal_shutdown_message" msgid="6142269839066172984">"Telefon sad radi normalno.\nDodirnite za više informacija"</string> <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefon se pregrijao, stoga se isključio kako bi se ohladio Telefon sada radi normalno.\n\nTelefon se može pregrijati ako:\n • upotrebljavate zahtjevne aplikacije (kao što su igre, aplikacije za videozapise ili navigaciju)\n • preuzimate ili prenosite velike datoteke\n • upotrebljavate telefon na visokim temperaturama."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Spremanje nije uspjelo. Pokušajte ponovo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Spremanje nije uspjelo."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Upotrijebite barem četiri znaka"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Upotrijebite manje od 16 znakova"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string> <string name="basic_status" msgid="2315371112182658176">"Otvoreni razgovor"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 36e4cd262386..25b30a77e51e 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Arc hitelesítve"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Megerősítve"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Koppintson a Megerősítés lehetőségre"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Zárolás arccal feloldva. Koppintson a folytatáshoz."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Arc felismerve. Koppintson a folytatáshoz."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"A mentés nem sikerült. Próbálja újra."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"A mentés nem sikerült."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Legalább négy karaktert használjon"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Legfeljebb 16 karaktert használhat"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string> <string name="basic_status" msgid="2315371112182658176">"Beszélgetés megnyitása"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 57b3033dc1a8..14326af3f2e9 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Դեմքը ճանաչվեց"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Հաստատվեց"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ավարտելու համար հպեք «Հաստատել»"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ապակողպվել է դեմքով։ Սեղմեք շարունակելու համար։"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Դեմքը ճանաչվեց։ Սեղմեք շարունակելու համար։"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Չհաջողվեց պահել։ Նորից փորձեք։"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Չհաջողվեց պահել։"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Օգտագործեք առնվազն 4 նիշ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Օգտագործեք ոչ ավել քան 16 նիշ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string> <string name="basic_status" msgid="2315371112182658176">"Բաց զրույց"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 4ece76749f97..f402df66558f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Wajah diautentikasi"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Dikonfirmasi"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ketuk Konfirmasi untuk menyelesaikan"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Kunci dibuka dengan wajah. Tekan untuk melanjutkan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Wajah dikenali. Tekan untuk melanjutkan."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat menyimpan. Coba lagi."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat menyimpan."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gunakan minimal 4 karakter"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gunakan kurang dari 16 karakter"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string> <string name="basic_status" msgid="2315371112182658176">"Membuka percakapan"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 29494ff4ed39..5b755070ee57 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Andlit staðfest"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Staðfest"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ýttu á „Staðfesta“ til að ljúka"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Opnað með andliti. Ýttu til að halda áfram."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Andlitið var greint. Ýttu til að halda áfram."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ekki hægt að vista. Reyndu aftur."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ekki hægt að vista."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Notaðu að minnsta kosti 4 stafi"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Notaðu færri en 16 stafi"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string> <string name="basic_status" msgid="2315371112182658176">"Opna samtal"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index f9b329e8f255..8937c212e3ca 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Volto autenticato"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confermato"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tocca Conferma per completare"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Sbloccato con il volto. Premi per continuare."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Volto riconosciuto. Premi per continuare."</string> @@ -913,7 +913,7 @@ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Mostra altre app"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Riordina"</string> <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Aggiungi controlli"</string> - <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Torna alle modifiche"</string> + <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Torna alla modifica"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"Impossibile caricare i controlli. Verifica nell\'app <xliff:g id="APP">%s</xliff:g> che le relative impostazioni non siano cambiate."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"Controlli compatibili non disponibili"</string> <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altro"</string> @@ -974,7 +974,7 @@ <string name="controls_menu_add" msgid="4447246119229920050">"Aggiungi controlli"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Modifica controlli"</string> <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Aggiungi app"</string> - <string name="controls_menu_remove" msgid="3006525275966023468">"Rimuovi l\'app"</string> + <string name="controls_menu_remove" msgid="3006525275966023468">"Rimuovi app"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Aggiungi uscite"</string> <string name="media_output_dialog_group" msgid="5571251347877452212">"Gruppo"</string> <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 dispositivo selezionato"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Impossibile salvare. Riprova."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Impossibile salvare."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Usa almeno 4 caratteri"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Usa meno di 16 caratteri"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string> <string name="basic_status" msgid="2315371112182658176">"Apri conversazione"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 6cf8ffa5d06f..271001dafd4b 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"זיהוי הפנים בוצע"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"יש אישור"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"יש להקיש על \'אישור\' לסיום התהליך"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"הנעילה בוטלה באמצעות זיהוי הפנים. יש ללחוץ כדי להמשיך."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"הפנים זוהו. יש ללחוץ כדי להמשיך."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"לא ניתן לשמור. כדאי לנסות שוב."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"לא ניתן לשמור."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"יש להזין 4 תווים לפחות"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"אפשר להזין עד 16 תווים"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"מספר Build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"מספר ה-Build הועתק ללוח."</string> <string name="basic_status" msgid="2315371112182658176">"פתיחת שיחה"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index a515e490b048..88d0c600c0ce 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"顔を認証しました"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"確認しました"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"完了するには [確認] をタップしてください"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"顔でロック解除しました。押して続行してください。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"顔を認識しました。押して続行してください。"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"保存できません。もう一度お試しください。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"保存できません。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"4 文字以上にしてください"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"使用できる文字数は 16 文字未満です"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string> <string name="basic_status" msgid="2315371112182658176">"空の会話"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index d37dfaf0d640..e7fd9d3e00fa 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"სახის ამოცნობილია"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"დადასტურებული"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"დასასრულებლად შეეხეთ „დადასტურებას“"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"განიბლოკა სახით. დააჭირეთ გასაგრძელებლად."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ამოცნობილია სახით. დააჭირეთ გასაგრძელებლად."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"შენახვა ვერ ხერხდება. ცადეთ ხელახლა."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"შენახვა ვერ ხერხდება."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"გამოიყენეთ მინიმუმ 4 სიმბოლო."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"გამოიყენეთ 16-ზე ნაკლები სიმბოლო"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაცვლის ბუფერში."</string> <string name="basic_status" msgid="2315371112182658176">"მიმოწერის გახსნა"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 64ceefd631c8..9964f4de7f8b 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Бет танылды."</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Расталды"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Аяқтау үшін \"Растау\" түймесін түртіңіз."</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Бетпен ашылды. Жалғастыру үшін басыңыз."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Бет танылды. Жалғастыру үшін басыңыз."</string> @@ -908,7 +908,7 @@ <string name="controls_favorite_default_title" msgid="967742178688938137">"Басқару элементтері"</string> <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Жылдам кіру үшін құрылғыны басқару элементтерін таңдаңыз."</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Басқару элементтерінің ретін өзгерту үшін оларды басып тұрып сүйреңіз."</string> - <string name="controls_favorite_removed" msgid="5276978408529217272">"Барлық басқару элементтері жойылды."</string> + <string name="controls_favorite_removed" msgid="5276978408529217272">"Барлық басқару элементі жойылды."</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Өзгерістер сақталмады."</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Басқа қолданбаларды көру"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Қайта реттеу"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Сақталмайды. Қайталап көріңіз."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Сақталмайды."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Кемінде 4 таңба пайдаланыңыз."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Ең көбі 16 таңба пайдаланыңыз."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Құрама нөмірі"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Құрама нөмірі буферге көшірілді."</string> <string name="basic_status" msgid="2315371112182658176">"Ашық әңгіме"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 43de595da0be..1165ba6e0b7d 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"បានផ្ទៀងផ្ទាត់មុខ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"បានបញ្ជាក់"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ចុច \"បញ្ជាក់\" ដើម្បីបញ្ចប់"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"បានដោះសោដោយប្រើមុខ។ សូមចុច ដើម្បីបន្ត។"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"បានស្គាល់មុខ។ សូមចុច ដើម្បីបន្ត។"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"មិនអាចរក្សាទុកបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"មិនអាចរក្សាទុកបានទេ។"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ប្រើយ៉ាងហោចណាស់ 4 តួអក្សរ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ប្រើតិចជាង 16 តួអក្សរ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខកំណែបង្កើត"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខកំណែបង្កើតទៅឃ្លីបបត។"</string> <string name="basic_status" msgid="2315371112182658176">"បើកការសន្ទនា"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 2ea15007649c..5d8e001e6153 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ದೃಢೀಕರಿಸಲಾಗಿದೆ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ಪೂರ್ಣಗೊಳಿಸಲು ದೃಢೀಕರಿಸಿ ಅನ್ನು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. ಮುಂದುವರಿಯಲು ಒತ್ತಿ."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ಮುಖ ಗುರುತಿಸಲಾಗಿದೆ. ಮುಂದುವರಿಯಲು ಒತ್ತಿ."</string> @@ -215,7 +215,7 @@ <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ಅಲಾರಮ್ಗಳು ಮಾತ್ರ"</string> - <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ಅಡಚಣೆ ಮಾಡಬೇಡ."</string> + <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ."</string> <string name="accessibility_quick_settings_bluetooth" msgid="8250942386687551283">"ಬ್ಲೂಟೂತ್."</string> <string name="accessibility_quick_settings_bluetooth_on" msgid="3819082137684078013">"ಬ್ಲೂಟೂತ್ ಆನ್ ಆಗಿದೆ."</string> <string name="accessibility_quick_settings_alarm" msgid="558094529584082090">"<xliff:g id="TIME">%s</xliff:g> ಗಂಟೆಗೆ ಅಲಾರಮ್ ಹೊಂದಿಸಲಾಗಿದೆ."</string> @@ -237,7 +237,7 @@ <string name="dessert_case" msgid="9104973640704357717">"ಡೆಸರ್ಟ್ ಕೇಸ್"</string> <string name="start_dreams" msgid="9131802557946276718">"ಸ್ಕ್ರೀನ್ ಸೇವರ್"</string> <string name="ethernet_label" msgid="2203544727007463351">"ಇಥರ್ನೆಟ್"</string> - <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ಅಡಚಣೆ ಮಾಡಬೇಡ"</string> + <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string> <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"ಬ್ಲೂಟೂತ್"</string> <string name="quick_settings_bluetooth_detail_empty_text" msgid="5760239584390514322">"ಯಾವುದೇ ಜೋಡಿಸಲಾದ ಸಾಧನಗಳು ಲಭ್ಯವಿಲ್ಲ"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string> @@ -683,7 +683,7 @@ <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string> <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"ಕ್ಯಾಲ್ಕ್ಯುಲೇಟರ್"</string> <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string> - <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ಅಡಚಣೆ ಮಾಡಬೇಡ"</string> + <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string> <string name="volume_dnd_silent" msgid="4154597281458298093">"ವಾಲ್ಯೂಮ್ ಬಟನ್ಗಳ ಶಾರ್ಟ್ಕಟ್"</string> <string name="battery" msgid="769686279459897127">"ಬ್ಯಾಟರಿ"</string> <string name="headset" msgid="4485892374984466437">"ಹೆಡ್ಸೆಟ್"</string> @@ -912,7 +912,7 @@ <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸಲಾಗಿಲ್ಲ"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ಇತರ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"ಮರುಹೊಂದಿಸಿ"</string> - <string name="controls_favorite_add_controls" msgid="1221420435546694004">"ಕಂಟ್ರೋಲ್ಗಳನ್ನು ಸೇರಿಸಿ"</string> + <string name="controls_favorite_add_controls" msgid="1221420435546694004">"ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಿ"</string> <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"ಎಡಿಟ್ ಮಾಡುವಿಕೆಗೆ ಹಿಂತಿರುಗಿ"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"ನಿಯಂತ್ರಣಗಳನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್ಗಳು ಬದಲಾಗಿಲ್ಲ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು <xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"ಹೊಂದಾಣಿಕೆಯ ನಿಯಂತ್ರಣಗಳು ಲಭ್ಯವಿಲ್ಲ"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ಕನಿಷ್ಠ 4 ಅಕ್ಷರಗಳನ್ನು ಬಳಸಿ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 ಕ್ಕಿಂತ ಕಡಿಮೆ ಅಕ್ಷರಗಳನ್ನು ಬಳಸಿ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಲ್ಲಿ ನಕಲಿಸಲಾಗಿದೆ."</string> <string name="basic_status" msgid="2315371112182658176">"ಸಂಭಾಷಣೆಯನ್ನು ತೆರೆಯಿರಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 945974453e74..7329f89ca47e 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"얼굴이 인증되었습니다."</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"확인함"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"완료하려면 확인을 탭하세요."</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"얼굴 인식으로 잠금 해제되었습니다. 계속하려면 누르세요."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"얼굴이 인식되었습니다. 계속하려면 누르세요."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"저장할 수 없습니다. 다시 시도해 주세요."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"저장할 수 없습니다."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"4자 이상 입력하세요."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16자 미만이어야 합니다."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"빌드 번호"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"빌드 번호가 클립보드에 복사되었습니다."</string> <string name="basic_status" msgid="2315371112182658176">"대화 열기"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index c5c448ec8d0a..751c98c9b3fa 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Жүздүн аныктыгы текшерилди"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Ырасталды"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Бүтүрүү үчүн \"Ырастоо\" баскычын басыңыз"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Кулпуну жүзүңүз менен ачтыңыз. Улантуу үчүн басыңыз."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Жүз таанылды. Улантуу үчүн басыңыз."</string> @@ -995,7 +995,7 @@ <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Кабарлоо кантип иштейт"</string> <string name="media_output_broadcast" msgid="3555580945878071543">"Кабарлоо"</string> <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Шайкеш Bluetooth түзмөктөрү болгон жакын жердеги кишилер кабарлап жаткан медиаңызды уга алышат"</string> - <string name="media_output_broadcasting_message" msgid="4150299923404886073">"Кабарыңызды угуу үчүн жакын жердеги кишилер шайкеш Bluetooth түзмөктөрү менен QR кодун скандап же кабарлоонун аталышын жана сырсөзүн колдоно алышат"</string> + <string name="media_output_broadcasting_message" msgid="4150299923404886073">"Кабарыңызды угуу үчүн жакын жердеги кишилер шайкеш Bluetooth түзмөктөрү менен QR кодду скандап же кабарлоонун аталышын жана сырсөзүн колдоно алышат"</string> <string name="media_output_broadcast_name" msgid="8786127091542624618">"Кабарлоонун аталышы"</string> <string name="media_output_broadcast_code" msgid="870795639644728542">"Сырсөз"</string> <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Сактоо"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Сакталган жок. Кайталап көрүңүз."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Сакталган жок."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Кеминде 4 символдон турушу керек"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 символдон ашпашы керек"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Курама номери"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Курама номери алмашуу буферине көчүрүлдү."</string> <string name="basic_status" msgid="2315371112182658176">"Ачык сүйлөшүү"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 6078b7bbffa1..e075dba9c37a 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ຢືນຢັນແລ້ວ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ແຕະຢືນຢັນເພື່ອສຳເລັດ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ປົດລັອກດ້ວຍໜ້າແລ້ວ. ກົດເພື່ອສືບຕໍ່."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ຈຳແນກໜ້າໄດ້ແລ້ວ. ກົດເພື່ອສືບຕໍ່."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ບໍ່ສາມາດບັນທຶກໄດ້. ກະລຸນາລອງໃໝ່."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ບໍ່ສາມາດບັນທຶກໄດ້."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ໃຊ້ຢ່າງໜ້ອຍ 4 ຕົວອັກສອນ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ໃຊ້ໜ້ອຍກວ່າ 16 ຕົວອັກສອນ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string> <string name="basic_status" msgid="2315371112182658176">"ເປີດການສົນທະນາ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index bb569e311ed3..57bb64e60073 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Veidas autentifikuotas"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Patvirtinta"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Paliesk. „Patvirtinti“, kad užbaigtumėte"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Atrakinta pagal veidą. Paspauskite, jei norite tęsti."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Veidas atpažintas. Paspauskite, jei norite tęsti."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nepavyko išsaugoti. Bandykite dar kartą."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nepavyko išsaugoti."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Naudokite bent 4 simbolius"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Naudokite daugiausia 16 simbolių"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijos numeris"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versijos numeris nukopijuotas į iškarpinę."</string> <string name="basic_status" msgid="2315371112182658176">"Atidaryti pokalbį"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 2acefead0545..78fc7b3761b6 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Seja autentificēta"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Apstiprināts"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Lai pabeigtu, pieskarieties Apstiprināt"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ierīce atbloķēta ar seju. Nospiediet, lai turpinātu."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Seja atpazīta. Nospiediet, lai turpinātu."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nevar saglabāt. Mēģiniet vēlreiz."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nevar saglabāt."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Izmantojiet vismaz 4 rakstzīmes"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Izmantojiet mazāk nekā 16 rakstzīmes"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string> <string name="basic_status" msgid="2315371112182658176">"Atvērt sarunu"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index b52af9bb6cd4..6ddd15c3b9f1 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лицето е проверено"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Потврдено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Допрете „Потврди“ за да се заврши"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Отклучено со лик. Притиснете за да продолжите."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лицето е препознаено. Притиснете за да продолжите."</string> @@ -174,12 +174,12 @@ <string name="fingerprint_re_enroll_dialog_title" msgid="3526033128113925780">"Поставување „Отклучување со отпечаток“"</string> <string name="fingerprint_re_enroll_dialog_content" msgid="4866561176695984879">"За да поставите „Отклучување со отпечаток“ повторно, вашите сегашни слики и модели на отпечаток ќе се избришат.\n\nОткако ќе се избришат, ќе треба повторно да поставите „Отклучување со отпечаток“ за да го користите отпечатокот за отклучување на телефонот или потврда дека сте вие."</string> <string name="fingerprint_re_enroll_dialog_content_singular" msgid="3083663339787381218">"За да поставите „Отклучување со отпечаток“ повторно, вашите сегашните слики и модели на отпечаток ќе бидат избришат.\n\nОткако ќе се избришат, ќе треба повторно да поставите „Отклучување со отпечаток“ за да го користите отпечатокот за да го отклучите телефонот или да потврдите дека сте вие."</string> - <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Не можеше да се постави „Отклучување со отпечаток“. Одете во „Поставки“ за да се обидете повторно."</string> + <string name="fingerprint_reenroll_failure_dialog_content" msgid="4733768492747300666">"Не можеше да се постави „Отклучување со отпечаток“. Отворете „Поставки“ за да се обидете повторно."</string> <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Поставете „Отклучување со лик“ повторно"</string> <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Отклучување со лик"</string> <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Поставување „Отклучување со лик“"</string> - <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Избришете го моменталниот модел на лице за да поставите „Отклучување со лик“ повторно.\n\nЌе треба повторно да ја поставите функцијава за да го користите ликот за да го отклучите телефонот."</string> - <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Не можеше да се постави „Отклучување со лик“. Одете во „Поставки“ за да се обидете повторно."</string> + <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"За да може одново да поставите „Отклучување со лик“, вашиот сегашен модел на лик ќе се избрише.\n\nЗа да го користите ликот за отклучување на телефонот, ќе треба повторно да ја поставите функцијава."</string> + <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Не можеше да се постави „Отклучување со лик“. Отворете „Поставки“ за да се обидете повторно."</string> <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Допрете го сензорот за отпечатоци"</string> <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Не се препознава ликот. Користете отпечаток."</string> <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) --> @@ -888,7 +888,7 @@ <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string> <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Премести долу лево"</string> <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Премести долу десно"</string> - <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Премести до работ и сокриј"</string> + <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Премести до работ и скриј"</string> <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Премести над работ и прикажи"</string> <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"Отстрани"</string> <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"вклучување/исклучување"</string> @@ -906,7 +906,7 @@ <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"означите како неомилена"</string> <string name="accessibility_control_move" msgid="8980344493796647792">"Преместете на позиција <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Контроли"</string> - <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Изберете контроли на уреди за брз пристап"</string> + <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Изберете контроли за уредите до кои ќе имате брз пристап"</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Задржете и влечете за да ги преуредите контролите"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"Сите контроли се отстранети"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Промените не се зачувани"</string> @@ -968,7 +968,7 @@ <string name="controls_error_removed" msgid="6675638069846014366">"Не е најдено"</string> <string name="controls_error_removed_title" msgid="1207794911208047818">"Контролата не е достапна"</string> <string name="controls_error_removed_message" msgid="2885911717034750542">"Не може да се пристапи до <xliff:g id="DEVICE">%1$s</xliff:g>. Проверете ја апликацијата <xliff:g id="APPLICATION">%2$s</xliff:g> за да се уверите дека контролата е сѐ уште достапна и дека поставките за апликацијата не се сменети."</string> - <string name="controls_open_app" msgid="483650971094300141">"Отвори апликација"</string> + <string name="controls_open_app" msgid="483650971094300141">"Отворете ја апликацијата"</string> <string name="controls_error_generic" msgid="352500456918362905">"Не може да се вчита статусот"</string> <string name="controls_error_failed" msgid="960228639198558525">"Грешка, обидете се повторно"</string> <string name="controls_menu_add" msgid="4447246119229920050">"Додајте контроли"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не може да се зачува. Обидете се повторно."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не може да се зачува."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Употребете најмалку 4 знаци"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Употребете помалку од 16 знаци"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број на верзија"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Бројот на верзијата е копиран во привремената меморија."</string> <string name="basic_status" msgid="2315371112182658176">"Започни разговор"</string> @@ -1145,7 +1146,7 @@ <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> <string name="call_from_work_profile_title" msgid="5418253516453177114">"Не може да повикувате од лична апликација"</string> - <string name="call_from_work_profile_text" msgid="2856337395968118274">"Вашата организацијата ви дозволува да упатувате повици само од работни апликации"</string> + <string name="call_from_work_profile_text" msgid="2856337395968118274">"Вашата организација ви дозволува да упатувате повици само од работни апликации"</string> <string name="call_from_work_profile_action" msgid="2937701298133010724">"Префрли се на работен профил"</string> <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"Инсталирајте работна апликација на телефон"</string> <string name="call_from_work_profile_close" msgid="5830072964434474143">"Откажи"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index ddd34be4fc17..467720ae18be 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"സ്ഥിരീകരിച്ചു"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"പൂർത്തിയാക്കാൻ സ്ഥിരീകരിക്കുക ടാപ്പ് ചെയ്യൂ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്തു. തുടരാൻ അമർത്തുക."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"മുഖം തിരിച്ചറിഞ്ഞു. തുടരാൻ അമർത്തുക."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"സംരക്ഷിക്കാൻ കഴിയില്ല. വീണ്ടും ശ്രമിക്കുക."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"സംരക്ഷിക്കാൻ കഴിയില്ല."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"കുറഞ്ഞത് 4 പ്രതീകങ്ങളെങ്കിലും ഉപയോഗിക്കുക"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16-ൽ കുറവ് പ്രതീകങ്ങൾ ഉപയോഗിക്കുക"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string> <string name="basic_status" msgid="2315371112182658176">"സംഭാഷണം തുറക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index bb9232477db8..d17933aff733 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Царайг баталгаажууллаа"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Баталгаажсан"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Дуусгахын тулд баталгаажуулахыг товших"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Царайгаар түгжээг тайлсан. Үргэлжлүүлэхийн тулд дарна уу."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Царайг таньсан. Үргэлжлүүлэхийн тулд дарна уу."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Хадгалах боломжгүй. Дахин оролдоно уу."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Хадгалах боломжгүй."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Дор хаяж 4 тэмдэгт ашиглана уу."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16-аас цөөн тэмдэгт ашиглана уу"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Хийцийн дугаар"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Хийцийн дугаарыг түр санах ойд хуулсан."</string> <string name="basic_status" msgid="2315371112182658176">"Харилцан яриаг нээх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 8c6fafe23e54..b1825e9e6096 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"चेहरा ऑथेंटिकेशन केलेला आहे"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"निश्चित केले"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"पूर्ण करण्यासाठी खात्री करा वर टॅप करा"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"चेहऱ्याने अनलॉक केले आहे. पुढे सुरू ठेवण्यासाठी प्रेस करा."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"चेहरा ओळखला आहे. पुढे सुरू ठेवण्यासाठी प्रेस करा."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"सेव्ह करू शकत नाही. पुन्हा प्रयत्न करा."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"सेव्ह करू शकत नाही."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"किमान चार वर्ण वापरा"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"१६ पेक्षा कमी वर्ण वापरा"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर क्लिपबोर्डवर कॉपी केला."</string> <string name="basic_status" msgid="2315371112182658176">"संभाषण उघडा"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index db4bff0fc47e..f12b476a4cab 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Wajah disahkan"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Disahkan"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Ketik Sahkan untuk menyelesaikan"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Dibuka kunci dengan wajah. Tekan untuk meneruskan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Wajah dicam. Tekan untuk meneruskan."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat disimpan. Cuba lagi."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat disimpan."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gunakan sekurang-kurangnya 4 aksara"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gunakan kurang daripada 16 aksara"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string> <string name="basic_status" msgid="2315371112182658176">"Buka perbualan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 36f634401773..1d85be1fca86 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"အတည်ပြုပြီးပြီ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"အပြီးသတ်ရန်အတွက် \'အတည်ပြုရန်\' ကို တို့ပါ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"မျက်နှာဖြင့် ဖွင့်ထားသည်။ ရှေ့ဆက်ရန် နှိပ်ပါ။"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"မျက်နှာ မှတ်မိသည်။ ရှေ့ဆက်ရန် နှိပ်ပါ။"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"သိမ်း၍မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"သိမ်း၍မရပါ။"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"အနည်းဆုံး အက္ခရာ ၄ လုံး သုံးရန်"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"အက္ခရာ ၁၆ လုံးအောက် သုံးရန်"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"တည်ဆောက်မှုနံပါတ်"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"တည်ဆောက်မှုနံပါတ်ကို ကလစ်ဘုတ်သို့ မိတ္တူကူးပြီးပါပြီ။"</string> <string name="basic_status" msgid="2315371112182658176">"စကားဝိုင်းကို ဖွင့်ရန်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 36bd3b4c0b90..2363a689777b 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansiktet er autentisert"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekreftet"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Trykk på Bekreft for å fullføre"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Låst opp med ansiktet. Trykk for å fortsette."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansiktet er gjenkjent. Trykk for å fortsette."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan ikke lagre. Prøv på nytt."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan ikke lagre."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Bruk minst 4 tegn"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Bruk færre enn 16 tegn"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string> <string name="basic_status" msgid="2315371112182658176">"Åpen samtale"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 98e020152350..2f99cc73979f 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"अनुहार प्रमाणीकरण गरियो"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"पुष्टि भयो"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"पूरा गर्नका लागि पुष्टि गर्नुहोस् नामक विकल्पमा ट्याप गर्नुहोस्"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"अनुहार प्रयोग गरी अनलक गरियो। जारी राख्न थिच्नुहोस्।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"अनुहार पहिचान गरियो। जारी राख्न थिच्नुहोस्।"</string> @@ -276,7 +276,7 @@ <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"बन्द गर्नुहोस्"</string> <string name="quick_settings_connected" msgid="3873605509184830379">"जोडिएको"</string> <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"यन्त्र जडान भयो, ब्याट्रीको चार्ज स्तर <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> - <string name="quick_settings_connecting" msgid="2381969772953268809">"जडान हुँदै..."</string> + <string name="quick_settings_connecting" msgid="2381969772953268809">"कनेक्ट गरिँदै छ..."</string> <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"हटस्पट"</string> <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"सक्रिय गर्दै…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"डेटा सेभर सक्रिय छ"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"सेभ गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"सेभ गर्न सकिएन।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"कम्तीमा ४ वटा वर्ण प्रयोग गर्नुहोस्"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"१६ वटाभन्दा कम वर्ण प्रयोग गर्नुहोस्"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नम्बर"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नम्बर कपी गरी क्लिपबोर्डमा सारियो।"</string> <string name="basic_status" msgid="2315371112182658176">"वार्तालाप खोल्नुहोस्"</string> @@ -1145,7 +1146,7 @@ <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string> <string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string> <string name="call_from_work_profile_title" msgid="5418253516453177114">"व्यक्तिगत एपमार्फत कल गर्न मिल्दैन"</string> - <string name="call_from_work_profile_text" msgid="2856337395968118274">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र कल गर्ने अनुमति दिन्छ"</string> + <string name="call_from_work_profile_text" msgid="2856337395968118274">"तपाईंको सङ्गठनले तपाईंलाई कामसम्बन्धी एपहरूमार्फत मात्र कल गर्ने अनुमति दिएको छ"</string> <string name="call_from_work_profile_action" msgid="2937701298133010724">"कार्य प्रोफाइल प्रयोग गर्नुहोस्"</string> <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"कामसम्बन्धी फोन एप इन्स्टल गर्नुहोस्"</string> <string name="call_from_work_profile_close" msgid="5830072964434474143">"रद्द गर्नुहोस्"</string> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 5efcef376817..99311e3d1e1b 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -100,4 +100,8 @@ <color name="accessibility_floating_menu_message_text">@*android:color/primary_text_default_material_dark</color> <color name="people_tile_background">@color/material_dynamic_secondary20</color> + + <!-- Internet Dialog --> + <color name="connected_network_primary_color">@color/material_dynamic_primary80</color> + <color name="connected_network_secondary_color">@color/material_dynamic_secondary80</color> </resources> diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml index 99bc794547f2..b6971d3c1fa4 100644 --- a/packages/SystemUI/res/values-night/styles.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -58,17 +58,13 @@ <style name="TextAppearance.InternetDialog.Active"> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:textSize">16sp</item> - <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> + <item name="android:textColor">@color/material_dynamic_primary80</item> <item name="android:textDirection">locale</item> </style> <style name="TextAppearance.InternetDialog.Secondary.Active"> <item name="android:textSize">14sp</item> - <item name="android:textColor">?android:attr/textColorSecondaryInverse</item> - </style> - - <style name="InternetDialog.Divider.Active"> - <item name="android:background">?android:attr/textColorSecondaryInverse</item> + <item name="android:textColor">@color/material_dynamic_secondary80</item> </style> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 53f0853981b2..05f7922c6d1c 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Gezicht geverifieerd"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bevestigd"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tik op Bevestigen om te voltooien"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Ontgrendeld via gezicht. Druk om door te gaan."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Gezicht herkend. Druk om door te gaan."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kan niet opslaan. Probeer het opnieuw."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kan niet opslaan."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gebruik minstens 4 tekens"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gebruik minder dan 16 tekens"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummer naar klembord gekopieerd."</string> <string name="basic_status" msgid="2315371112182658176">"Gesprek openen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index c33c8ba47c3e..7f443de1898f 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ମୁହଁ ପ୍ରାମାଣିକତା ହୋଇଛି"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ସୁନିଶ୍ଚିତ କରାଯାଇଛି"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ସମ୍ପୂର୍ଣ୍ଣ କରିବାକୁ ସୁନିଶ୍ଚିତ କରନ୍ତୁରେ ଟାପ୍ କରନ୍ତୁ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ ଦବାନ୍ତୁ।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ଫେସ ଚିହ୍ନଟ କରାଯାଇଛି। ଜାରି ରଖିବାକୁ ଦବାନ୍ତୁ।"</string> @@ -257,7 +257,7 @@ <string name="quick_settings_media_device_label" msgid="8034019242363789941">"ମିଡିଆ ଡିଭାଇସ୍"</string> <string name="quick_settings_user_title" msgid="8673045967216204537">"ଉପଯୋଗକର୍ତ୍ତା"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string> - <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟରନେଟ୍"</string> + <string name="quick_settings_internet_label" msgid="6603068555872455463">"ଇଣ୍ଟରନେଟ"</string> <string name="quick_settings_networks_available" msgid="1875138606855420438">"ନେଟୱାର୍କଗୁଡ଼ିକ ଉପଲବ୍ଧ"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"ନେଟୱାର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"କୌଣସି ୱାଇ-ଫାଇ ନେଟ୍ୱର୍କ ଉପଲବ୍ଧ ନାହିଁ"</string> @@ -297,7 +297,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"<xliff:g id="TIME">%s</xliff:g>ରେ ଅନ୍ ହେବ"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"<xliff:g id="TIME">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"ଗାଢ଼ା ଥିମ"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ବ୍ୟାଟେରୀ ସେଭର୍"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"ବେଟେରୀ ସେଭର"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"ସନ୍ଧ୍ୟାରେ ଚାଲୁ ହେବ"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"ସକାଳ ପର୍ଯ୍ୟନ୍ତ"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"<xliff:g id="TIME">%s</xliff:g>ରେ ଚାଲୁ ହେବ"</string> @@ -415,7 +415,7 @@ <string name="media_projection_entry_generic_permission_dialog_continue" msgid="8640381403048097116">"ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ଦ୍ୱାରା ବ୍ଲକ କରାଯାଇଛି"</string> <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"ଡିଭାଇସ ନୀତି ଦ୍ୱାରା ସ୍କ୍ରିନ କେପଚରିଂକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string> - <string name="clear_all_notifications_text" msgid="348312370303046130">"ସମସ୍ତ ଖାଲି କରନ୍ତୁ"</string> + <string name="clear_all_notifications_text" msgid="348312370303046130">"ସବୁ ଖାଲି କରନ୍ତୁ"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"ଇତିହାସ"</string> <string name="notification_section_header_incoming" msgid="850925217908095197">"ନୂଆ"</string> @@ -529,7 +529,7 @@ <string name="enable_demo_mode" msgid="3180345364745966431">"ଡେମୋ ମୋଡ୍ ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="show_demo_mode" msgid="3677956462273059726">"ଡେମୋ ମୋଡ୍ ଦେଖାନ୍ତୁ"</string> <string name="status_bar_ethernet" msgid="5690979758988647484">"ଇଥରନେଟ୍"</string> - <string name="status_bar_alarm" msgid="87160847643623352">"ଆଲାର୍ମ"</string> + <string name="status_bar_alarm" msgid="87160847643623352">"ଆଲାରାମ"</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> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ସେଭ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ସେଭ କରାଯାଇପାରିଲା ନାହିଁ।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ଅତିକମରେ 4ଟି କେରେକ୍ଟର ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16ଟିରୁ କମ କେରେକ୍ଟର ବ୍ୟବହାର କରନ୍ତୁ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ବିଲ୍ଡ ନମ୍ୱର"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"କ୍ଲିପବୋର୍ଡକୁ କପି କରାଯାଇଥିବା ବିଲ୍ଡ ନମ୍ୱର।"</string> <string name="basic_status" msgid="2315371112182658176">"ବାର୍ତ୍ତାଳାପ ଖୋଲନ୍ତୁ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 281b11a6f6c9..f7bb0c916458 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ਚਿਹਰਾ ਪ੍ਰਮਾਣੀਕਿਰਤ ਹੋਇਆ"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ਪੁਸ਼ਟੀ ਕੀਤੀ ਗਈ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"ਪੂਰਾ ਕਰਨ ਲਈ ਪੁਸ਼ਟੀ ਕਰੋ \'ਤੇ ਟੈਪ ਕਰੋ"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ। ਜਾਰੀ ਰੱਖਣ ਲਈ ਦਬਾਓ।"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਹੋਈ। ਜਾਰੀ ਰੱਖਣ ਲਈ ਦਬਾਓ।"</string> @@ -276,7 +276,7 @@ <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"ਬੰਦ ਕਰੋ"</string> <string name="quick_settings_connected" msgid="3873605509184830379">"ਕਨੈਕਟ ਕੀਤਾ"</string> <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"ਕਨੈਕਟ ਕੀਤੀ ਗਈ, ਬੈਟਰੀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> - <string name="quick_settings_connecting" msgid="2381969772953268809">"ਕਨੈਕਟ ਕਰ ਰਿਹਾ ਹੈ..."</string> + <string name="quick_settings_connecting" msgid="2381969772953268809">"ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string> <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"ਹੌਟਸਪੌਟ"</string> <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"ਡਾਟਾ ਸੇਵਰ ਚਾਲੂ ਹੈ"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ਘੱਟੋ-ਘੱਟ 4 ਅੱਖਰ-ਚਿੰਨ੍ਹ ਵਰਤੋ"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 ਤੋਂ ਘੱਟ ਅੱਖਰ-ਚਿੰਨ੍ਹ ਵਰਤੋ"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"ਬਿਲਡ ਨੰਬਰ"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"ਬਿਲਡ ਨੰਬਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ।"</string> <string name="basic_status" msgid="2315371112182658176">"ਗੱਲਬਾਤ ਖੋਲ੍ਹੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 8a9608f8b862..e6af3fcf5f90 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Twarz rozpoznana"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potwierdzono"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Aby zakończyć, kliknij Potwierdź"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odblokowano rozpoznawaniem twarzy. Kliknij, aby kontynuować."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Twarz rozpoznana. Kliknij, aby kontynuować."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nie można zapisać. Spróbuj ponownie."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nie można zapisać."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Wpisz co najmniej 4 znaki"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Wpisz mniej niż 16 znaków"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji został skopiowany do schowka."</string> <string name="basic_status" msgid="2315371112182658176">"Otwarta rozmowa"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 401843350f4c..b9df66103f3f 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Rosto autenticado"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toque em \"Confirmar\" para concluir"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado pelo rosto. Pressione para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Pressione para continuar."</string> @@ -989,7 +989,7 @@ <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string> - <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string> + <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompa sua sessão compartilhada para transferir mídia a outro dispositivo"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Falha ao salvar. Tente de novo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Falha ao salvar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use pelo menos 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 2d9c7d3b0020..9c11d6150cd8 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Rosto autenticado"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmado"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toque em Confirmar para concluir."</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado com o rosto. Prima para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Prima para continuar."</string> @@ -908,7 +908,7 @@ <string name="controls_favorite_default_title" msgid="967742178688938137">"Controlos"</string> <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Escolha os controlos de dispositivos para aceder rapidamente"</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Toque sem soltar e arraste para reorganizar os controlos"</string> - <string name="controls_favorite_removed" msgid="5276978408529217272">"Todos os controlos foram removidos."</string> + <string name="controls_favorite_removed" msgid="5276978408529217272">"Todos os controlos foram removidos"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Alterações não guardadas."</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Ver outras apps"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Reorganizar"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Não é possível guardar. Tente novamente."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Não é possível guardar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use, pelo menos, 4 carateres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 carateres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Abrir conversa"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 401843350f4c..b9df66103f3f 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Rosto autenticado"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmada"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Toque em \"Confirmar\" para concluir"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Desbloqueado pelo rosto. Pressione para continuar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Rosto reconhecido. Pressione para continuar."</string> @@ -989,7 +989,7 @@ <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volume"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Alto-falantes e telas"</string> - <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispositivos sugeridos"</string> + <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Opções de dispositivos"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Interrompa sua sessão compartilhada para transferir mídia a outro dispositivo"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Parar"</string> <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Como funciona a transmissão"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Falha ao salvar. Tente de novo."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Falha ao salvar."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Use pelo menos 4 caracteres"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Use menos de 16 caracteres"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da versão"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string> <string name="basic_status" msgid="2315371112182658176">"Conversa aberta"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 060f6d7db0d0..9667f43a58c7 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Chip autentificat"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmat"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Atinge Confirm pentru a finaliza"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S-a deblocat cu ajutorul feței. Apasă pentru a continua."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Chipul a fost recunoscut. Apasă pentru a continua."</string> @@ -988,7 +988,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"Dispozitive disponibile pentru ieșire audio."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"Volum"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și afișaje"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"Difuzoare și ecrane"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"Dispozitive sugerate"</string> <string name="media_output_end_session_dialog_summary" msgid="5954520685989877347">"Oprește sesiunea comună ca să muți elementul media pe alt dispozitiv"</string> <string name="media_output_end_session_dialog_stop" msgid="208189434474624412">"Oprește"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nu se poate salva. Încearcă din nou."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nu se poate salva."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Folosește minimum 4 caractere."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Folosește maximum 16 caractere"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string> <string name="basic_status" msgid="2315371112182658176">"Deschide conversația"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index d6530fbc1ca6..8945e34232b1 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лицо распознано"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Подтверждено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Нажмите \"Подтвердить\""</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Разблокировано сканированием лица. Нажмите, чтобы продолжить."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лицо распознано. Нажмите, чтобы продолжить."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не удалось сохранить. Повторите попытку."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не удалось сохранить."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Минимальное количество символов – 4"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Максимальное количество символов – 16"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string> <string name="basic_status" msgid="2315371112182658176">"Открытый чат"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index a5c1a18c54a5..ef291cba38e8 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"මුහුණ සත්යාපන කළා"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"තහවුරු කළා"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"සම්පූර්ණ කිරීමට තහවුරු කරන්න තට්ටු කර."</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"මුහුණ මගින් අගුලු හරින ලදි. ඉදිරියට යාමට ඔබන්න."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"මුහුණ හඳුනා ගන්නා ලදි. ඉදිරියට යාමට ඔබන්න."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"සුරැකිය නොහැකිය. නැවත උත්සාහ කරන්න."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"සුරැකිය නොහැකිය."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"අවම වශයෙන් අනුලකුණු 4ක් භාවිතා කරන්න"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"අනුලකුණු 16කට වඩා අඩුවෙන් භාවිතා කරන්න"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිමැවුම් අංකය"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"නිමැවුම් අංකය පසුරු පුවරුවට පිටපත් කරන ලදි."</string> <string name="basic_status" msgid="2315371112182658176">"සංවාදය විවෘත කරන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index e60dd5f277dd..2544505e4d4b 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Tvár bola overená"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potvrdené"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Overenie dokončíte klepnutím na Potvrdiť"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odomknuté tvárou. Pokračujte stlačením."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Tvár bola rozpoznaná. Pokračujte stlačením."</string> @@ -721,7 +721,7 @@ <string name="left_icon" msgid="5036278531966897006">"Ľavá ikona"</string> <string name="right_icon" msgid="1103955040645237425">"Pravá ikona"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pridržaním a presunutím pridáte karty"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Dlaždice môžete usporiadať pridržaním a presunutím"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Karty môžete usporiadať pridržaním a presunutím"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Presunutím sem odstránite"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimálny počet vyžadovaných dlaždíc: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Upraviť"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nedá sa uložiť. Skúste to znova."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nedá sa uložiť."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Použite aspoň štyri znaky"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Použite menej než 16 znakov"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string> <string name="basic_status" msgid="2315371112182658176">"Otvorená konverzácia"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index dcc34c4de585..9a9676fec3e3 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Pristnost obraza je potrjena"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Potrjeno"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Za dokončanje se dotaknite »Potrdite«"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Odklenjeno z obrazom. Pritisnite za nadaljevanje."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Obraz je prepoznan. Pritisnite za nadaljevanje."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ni mogoče shraniti. Poskusite znova."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ni mogoče shraniti."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Uporabite vsaj 4 znake."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Uporabite manj kot 16 znakov."</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odložišče."</string> <string name="basic_status" msgid="2315371112182658176">"Odprt pogovor"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index f851d307c97c..d477aa415067 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Fytyra u vërtetua"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Konfirmuar"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Trokit \"Konfirmo\" për ta përfunduar"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"U shkyç me fytyrë. Shtyp për të vazhduar."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Fytyra u njoh. Shtyp për të vazhduar."</string> @@ -907,7 +907,7 @@ <string name="accessibility_control_move" msgid="8980344493796647792">"Zhvendose te pozicioni <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Kontrollet"</string> <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Zgjidh kontrollet e pajisjes për të pasur qasje me shpejtësi"</string> - <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mbaje të shtypur dhe zvarrit për të risistemuar kontrollet"</string> + <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mbaje shtypur dhe zvarrit për të riorganizuar kontrollet"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"Të gjitha kontrollet u hoqën"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Ndryshimet nuk u ruajtën"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Shiko aplikacionet e tjera"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nuk mund të ruhet. Provo përsëri."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nuk mund të ruhet."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Përdor të paktën 4 karaktere"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Përdor më pak se 16 karaktere"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string> <string name="basic_status" msgid="2315371112182658176">"Hap bisedën"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 576cad09aae2..b293e6aaae02 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Лице је потврђено"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Потврђено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Додирните Потврди да бисте завршили"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Откључано је лицем. Притисните да бисте наставили."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Лице је препознато. Притисните да бисте наставили."</string> @@ -210,7 +210,7 @@ <string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"Прозор са обавештењима."</string> <string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Брза подешавања."</string> <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"Брза подешавања и трака са обавештењима."</string> - <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Закључан екран."</string> + <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Закључан екран"</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Закључан екран за посао"</string> <string name="accessibility_desc_close" msgid="8293708213442107755">"Затвори"</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"потпуна тишина"</string> @@ -785,7 +785,7 @@ <string name="tuner_right" msgid="8247571132790812149">"Стрелица удесно"</string> <string name="tuner_menu" msgid="363690665924769420">"Мени"</string> <string name="tuner_app" msgid="6949280415826686972">"Апликација <xliff:g id="APP">%1$s</xliff:g>"</string> - <string name="notification_channel_alerts" msgid="3385787053375150046">"Обавештења"</string> + <string name="notification_channel_alerts" msgid="3385787053375150046">"Упозорења"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батерија"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Снимци екрана"</string> <string name="notification_channel_instant" msgid="7556135423486752680">"Инстант апликације"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Чување није успело. Пробајте поново."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Чување није успело."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Користите бар 4 знака"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Користите мање од 16 знакова"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string> <string name="basic_status" msgid="2315371112182658176">"Отворите конверзацију"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 8fe491b62699..00a2e3c158bb 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansiktet har autentiserats"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekräftat"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Slutför genom att trycka på Bekräfta"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Upplåst med ansiktslås. Tryck för att fortsätta."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansiktet har identifierats. Tryck för att fortsätta."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Det gick inte att spara. Försök igen."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Det gick inte att spara."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Använd minst 4 tecken"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Använd färre än 16 tecken"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string> <string name="basic_status" msgid="2315371112182658176">"Öppen konversation"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index e359c01be04c..af90c67aefb9 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Uso umethibitishwa"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Imethibitishwa"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Gusa Thibitisha ili ukamilishe"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Imefunguliwa kwa kutumia uso wako. Bonyeza ili uendelee."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Uso umetambuliwa. Bonyeza ili uendelee."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Imeshindwa kuhifadhi. Jaribu tena."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Imeshindwa kuhifadhi."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Tumia angalau herufi 4"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Tumia herufi chini ya 16"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string> <string name="basic_status" msgid="2315371112182658176">"Fungua mazungumzo"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 7dcd0fb6983d..fd33af97be2c 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"முகம் அங்கீகரிக்கப்பட்டது"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"உறுதிப்படுத்தப்பட்டது"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"முடிக்க \'உறுதிப்படுத்துக\' என்பதை தட்டவும்"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"முகம் மூலம் அன்லாக் செய்யப்பட்டது. தொடர அழுத்தவும்."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"முகம் அங்கீகரிக்கப்பட்டது. தொடர அழுத்தவும்."</string> @@ -972,7 +972,7 @@ <string name="controls_error_generic" msgid="352500456918362905">"நிலையைக் காட்ட முடியவில்லை"</string> <string name="controls_error_failed" msgid="960228639198558525">"பிழை, மீண்டும் முயலவும்"</string> <string name="controls_menu_add" msgid="4447246119229920050">"கட்டுப்பாடுகளைச் சேர்த்தல்"</string> - <string name="controls_menu_edit" msgid="890623986951347062">"கட்டுப்பாடுகளை மாற்றுதல்"</string> + <string name="controls_menu_edit" msgid="890623986951347062">"கட்டுப்பாடுகளை மாற்றுக"</string> <string name="controls_menu_add_another_app" msgid="8661172304650786705">"ஆப்ஸைச் சேர்"</string> <string name="controls_menu_remove" msgid="3006525275966023468">"ஆப்ஸை அகற்று"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"அவுட்புட்களைச் சேர்த்தல்"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"சேமிக்க முடியவில்லை. மீண்டும் முயலவும்."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"சேமிக்க முடியவில்லை."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"குறைந்தது 4 எழுத்துகளைப் பயன்படுத்துங்கள்"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 எழுத்துகளுக்குக் குறைவாகப் பயன்படுத்துங்கள்"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"பதிப்பு எண்"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"பதிப்பு எண் கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது."</string> <string name="basic_status" msgid="2315371112182658176">"திறந்தநிலை உரையாடல்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 9680b3ecfd8d..63fc9e03bfce 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ముఖం ప్రామాణీకరించబడింది"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"నిర్ధారించబడింది"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"పూర్తి చేయడానికి \"నిర్ధారించు\" నొక్కండి"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ముఖం ద్వారా అన్లాక్ చేయబడింది. కొనసాగించడానికి నొక్కండి."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"ముఖం గుర్తించబడింది. కొనసాగించడానికి నొక్కండి."</string> @@ -151,7 +151,7 @@ <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"పిన్ను ఉపయోగించండి"</string> <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"ఆకృతిని ఉపయోగించండి"</string> <string name="biometric_dialog_use_password" msgid="3445033859393474779">"పాస్వర్డ్ను ఉపయోగించండి"</string> - <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"పిన్ తప్పు"</string> + <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN తప్పు"</string> <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ఆకృతి తప్పు"</string> <string name="biometric_dialog_wrong_password" msgid="69477929306843790">"పాస్వర్డ్ తప్పు"</string> <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"చాలా ఎక్కువ తప్పు ప్రయత్నాలు చేశారు.\n<xliff:g id="NUMBER">%d</xliff:g> సెకన్ల తర్వాత మళ్లీ ట్రై చేయండి."</string> @@ -390,7 +390,7 @@ <string name="guest_notification_session_active" msgid="5567273684713471450">"మీరు గెస్ట్ మోడ్లో ఉన్నారు"</string> <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"కొత్త యూజర్ను జోడించడం వలన గెస్ట్ మోడ్ నుండి వైదొలుగుతుంది. అలాగే ప్రస్తుత గెస్ట్ సెషన్ నుండి అన్ని యాప్లతో పాటు మొత్తం డేటా తొలగించబడుతుంది."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"వినియోగదారు పరిమితిని చేరుకున్నారు"</string> - <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ఒక యూజర్ను మాత్రమే క్రియేట్ చేయవచ్చు.}other{మీరు గరిష్టంగా # మంది యూజర్లను జోడించవచ్చు.}}"</string> + <string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{ఒక యూజర్ను మాత్రమే క్రియేట్ చేయవచ్చు.}other{మీరు గరిష్ఠంగా # మంది యూజర్లను జోడించవచ్చు.}}"</string> <string name="user_remove_user_title" msgid="9124124694835811874">"యూజర్ను తీసివేయాలా?"</string> <string name="user_remove_user_message" msgid="6702834122128031833">"ఈ వినియోగదారుకు సంబంధించిన అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> <string name="user_remove_user_remove" msgid="8387386066949061256">"తీసివేయండి"</string> @@ -930,7 +930,7 @@ <string name="controls_settings_dialog_positive_button" msgid="436070672551674863">"అవును"</string> <string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"పిన్ అక్షరాలను లేదా చిహ్నాలను కలిగి ఉంది"</string> <string name="controls_pin_verify" msgid="3452778292918877662">"<xliff:g id="DEVICE">%s</xliff:g>ను వెరిఫై చేయండి"</string> - <string name="controls_pin_wrong" msgid="6162694056042164211">"పిన్ తప్పు"</string> + <string name="controls_pin_wrong" msgid="6162694056042164211">"PIN తప్పు"</string> <string name="controls_pin_instructions" msgid="6363309783822475238">"పిన్ని ఎంటర్ చేయండి"</string> <string name="controls_pin_instructions_retry" msgid="1566667581012131046">"మరొక పిన్ని ప్రయత్నించండి"</string> <string name="controls_confirmation_message" msgid="7744104992609594859">"<xliff:g id="DEVICE">%s</xliff:g>కి సంబంధించి మార్పును నిర్ధారించండి"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"సేవ్ చేయడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"సేవ్ చేయడం సాధ్యపడదు."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"కనీసం 4 అక్షరాలను ఉపయోగించండి"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 కంటే తక్కువ అక్షరాలను ఉపయోగించండి"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"బిల్డ్ నంబర్"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"బిల్డ్ నంబర్, క్లిప్బోర్డ్కు కాపీ చేయబడింది."</string> <string name="basic_status" msgid="2315371112182658176">"సంభాషణను తెరవండి"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 93365fe4f95f..49f0f2a85802 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ตรวจสอบสิทธิ์ใบหน้าแล้ว"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"ยืนยันแล้ว"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"แตะยืนยันเพื่อดำเนินการให้เสร็จสมบูรณ์"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"ปลดล็อกด้วยใบหน้าแล้ว กดเพื่อดำเนินการต่อ"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"จดจำใบหน้าได้ กดเพื่อดำเนินการต่อ"</string> @@ -907,12 +907,12 @@ <string name="accessibility_control_move" msgid="8980344493796647792">"ย้ายไปที่ตำแหน่ง <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"การควบคุม"</string> <string name="controls_favorite_subtitle" msgid="5818709315630850796">"เลือกระบบควบคุมอุปกรณ์ที่ต้องการให้เข้าถึงได้อย่างรวดเร็ว"</string> - <string name="controls_favorite_rearrange" msgid="5616952398043063519">"แตะตัวควบคุมค้างไว้แล้วลากเพื่อจัดเรียงใหม่"</string> - <string name="controls_favorite_removed" msgid="5276978408529217272">"นำตัวควบคุมทั้งหมดออกแล้ว"</string> + <string name="controls_favorite_rearrange" msgid="5616952398043063519">"แตะระบบควบคุมค้างไว้แล้วลากเพื่อจัดเรียงใหม่"</string> + <string name="controls_favorite_removed" msgid="5276978408529217272">"นำระบบควบคุมทั้งหมดออกแล้ว"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ยังไม่ได้บันทึกการเปลี่ยนแปลง"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ดูแอปอื่นๆ"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"จัดเรียงใหม่"</string> - <string name="controls_favorite_add_controls" msgid="1221420435546694004">"เพิ่มตัวควบคุม"</string> + <string name="controls_favorite_add_controls" msgid="1221420435546694004">"เพิ่มระบบควบคุม"</string> <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"กลับไปที่การแก้ไข"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"โหลดตัวควบคุมไม่ได้ ตรวจสอบแอป <xliff:g id="APP">%s</xliff:g> ให้แน่ใจว่าการตั้งค่าของแอปไม่เปลี่ยนแปลง"</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"ตัวควบคุมที่เข้ากันได้ไม่พร้อมใช้งาน"</string> @@ -972,7 +972,7 @@ <string name="controls_error_generic" msgid="352500456918362905">"โหลดสถานะไม่ได้"</string> <string name="controls_error_failed" msgid="960228639198558525">"พบข้อผิดพลาด โปรดลองอีกครั้ง"</string> <string name="controls_menu_add" msgid="4447246119229920050">"เพิ่มตัวควบคุม"</string> - <string name="controls_menu_edit" msgid="890623986951347062">"แก้ไขตัวควบคุม"</string> + <string name="controls_menu_edit" msgid="890623986951347062">"แก้ไขระบบควบคุม"</string> <string name="controls_menu_add_another_app" msgid="8661172304650786705">"เพิ่มแอป"</string> <string name="controls_menu_remove" msgid="3006525275966023468">"นำแอปออก"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"เพิ่มเอาต์พุต"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"บันทึกไม่ได้ โปรดลองอีกครั้ง"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"บันทึกไม่ได้"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"ใช้อักขระอย่างน้อย 4 ตัว"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"ใช้อักขระไม่เกิน 16 ตัว"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิลด์"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิลด์ไปยังคลิปบอร์ดแล้ว"</string> <string name="basic_status" msgid="2315371112182658176">"เปิดการสนทนา"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index fec9aad5aaa0..755c87207b9a 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Na-authenticate ang mukha"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Nakumpirma"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"I-tap ang Kumpirmahin para kumpletuhin"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Na-unlock gamit ang mukha. Pindutin para magpatuloy."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Nakilala ang mukha. Pindutin para magpatuloy."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Hindi ma-save. Subukan ulit."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Hindi ma-save."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Gumamit ng hindi bababa sa 4 na character"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Gumamit ng mas kaunti sa 16 na character"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string> <string name="basic_status" msgid="2315371112182658176">"Buksan ang pag-uusap"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index b5201fe43d6a..72c2d436d9b8 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Yüz kimliği doğrulandı"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Onaylandı"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tamamlamak için Onayla\'ya dokunun"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Cihazın kilidini yüzünüzle açtınız. Devam etmek için basın."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Yüzünüz tanındı. Devam etmek için basın."</string> @@ -281,7 +281,7 @@ <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"Açılıyor…"</string> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Veri Tasarrufu açık"</string> <string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# cihaz}other{# cihaz}}"</string> - <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Fener"</string> + <string name="quick_settings_flashlight_label" msgid="4904634272006284185">"El feneri"</string> <string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Kamera kullanımda"</string> <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Mobil veri"</string> <string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Veri kullanımı"</string> @@ -912,7 +912,7 @@ <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Değişiklikler kaydedilmedi"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Tüm uygulamaları göster"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Yeniden düzenle"</string> - <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Kontrol ekle"</string> + <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Denetim ekle"</string> <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Düzenlemeye dön"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"Kontroller yüklenemedi. Uygulama ayarlarının değişmediğinden emin olmak için <xliff:g id="APP">%s</xliff:g> uygulamasını kontrol edin."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"Uyumlu kontrol bulunamadı"</string> @@ -923,7 +923,7 @@ <string name="controls_dialog_message" msgid="342066938390663844">"<xliff:g id="APP">%s</xliff:g> tarafından önerildi"</string> <string name="controls_tile_locked" msgid="731547768182831938">"Cihaz kilitlendi"</string> <string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"Cihazlar kilit ekranında gösterilip buradan kontrol edilsin mi?"</string> - <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"Kilit ekranına harici cihazlarınız için kontroller ekleyebilirsiniz.\n\nCihaz uygulamanız, bazı cihazları telefonunuzun veya tabletinizin kilidini açmadan kontrol etmenize izin verebilir.\n\nAyarlar\'da istediğiniz zaman değişiklik yapabilirsiniz."</string> + <string name="controls_settings_show_controls_dialog_message" msgid="7666211700524587969">"Kilit ekranına harici cihazlarınız için denetimler ekleyebilirsiniz.\n\nCihaz uygulamanız, bazı cihazları telefonunuzun veya tabletinizin kilidini açmadan denetlemenize izin verebilir.\n\nAyarlar\'da istediğiniz zaman değişiklik yapabilirsiniz."</string> <string name="controls_settings_trivial_controls_dialog_title" msgid="7593188157655036677">"Cihazlar kilit ekranından kontrol edilsin mi?"</string> <string name="controls_settings_trivial_controls_dialog_message" msgid="397178734990952575">"Bazı cihazları telefonunuzun veya tabletinizin kilidini açmadan kontrol edebilirsiniz.Hangi cihazların bu şekilde kontrol edilebileceğini cihaz uygulamanız belirler."</string> <string name="controls_settings_dialog_neutral_button" msgid="4514446354793124140">"Hayır, teşekkürler"</string> @@ -974,7 +974,7 @@ <string name="controls_menu_add" msgid="4447246119229920050">"Denetim ekle"</string> <string name="controls_menu_edit" msgid="890623986951347062">"Denetimleri düzenle"</string> <string name="controls_menu_add_another_app" msgid="8661172304650786705">"Uygulama ekle"</string> - <string name="controls_menu_remove" msgid="3006525275966023468">"Uygulamayı kaldır"</string> + <string name="controls_menu_remove" msgid="3006525275966023468">"Uygulama kaldır"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Çıkışlar ekleyin"</string> <string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string> <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçildi"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Kaydedilemiyor. Tekrar deneyin."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Kaydedilemiyor."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"En az 4 karakter kullanın."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"En fazla 16 karakter kullanın"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string> <string name="basic_status" msgid="2315371112182658176">"Görüşmeyi aç"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index e7d7e2e7e234..c1e291d3fce7 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Обличчя автентифіковано"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Підтверджено"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Щоб завершити, натисніть \"Підтвердити\""</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Розблоковано (фейсконтроль). Натисніть, щоб продовжити."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Обличчя розпізнано. Натисніть, щоб продовжити."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Не вдалося зберегти. Повторіть спробу."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Не вдалося зберегти."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Введіть принаймні 4 символи"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Введіть менше ніж 16 символів"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер складання"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Номер складання скопійовано в буфер обміну."</string> <string name="basic_status" msgid="2315371112182658176">"Відкрита розмова"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index a67cc5a444ef..46596c34a250 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چہرے کی تصدیق ہو گئی"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تصدیق شدہ"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"مکمل کرنے کیلئے \'تصدیق کریں\' تھپتھپائیں"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"چہرے سے انلاک کیا گیا۔ جاری رکھنے کے لیے دبائیں۔"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"چہرے کی شناخت ہو گئی۔ جاری رکھنے کے لیے دبائیں۔"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"محفوظ نہیں کیا جا سکا۔ پھر کوشش کریں۔"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"محفوظ نہیں کیا جا سکا۔"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"کم از کم 4 حروف استعمال کریں"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"16 حروف سے کم استعمال کریں"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"بلڈ نمبر"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string> <string name="basic_status" msgid="2315371112182658176">"گفتگو کھولیں"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 86dc3325ae13..ab1fe063a797 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Yuzingiz aniqlandi"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Tasdiqlangan"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Tasdiqlash uchun tegining"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Yuz orqali ochildi. Davom etish uchun bosing."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Yuz aniqlandi. Davom etish uchun bosing."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Saqlanmadi. Qayta urining."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Saqlanmadi."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Parolga kamida 4 ta belgi kiriting."</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Kiritiladigan belgilar 16 tadan oshmasin"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string> <string name="basic_status" msgid="2315371112182658176">"Suhbatni ochish"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index e4073e4f898e..e681b1a724a0 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Đã xác thực khuôn mặt"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Ðã xác nhận"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Nhấn vào Xác nhận để hoàn tất"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Đã mở khoá bằng khuôn mặt. Hãy nhấn để tiếp tục."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Đã nhận diện khuôn mặt. Hãy nhấn để tiếp tục."</string> @@ -907,12 +907,12 @@ <string name="accessibility_control_move" msgid="8980344493796647792">"Di chuyển tới vị trí số <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Các tùy chọn điều khiển"</string> <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Chọn các chế độ điều khiển thiết bị để truy cập nhanh"</string> - <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Giữ và kéo để sắp xếp lại các tuỳ chọn điều khiển"</string> - <string name="controls_favorite_removed" msgid="5276978408529217272">"Đã xóa tất cả tùy chọn điều khiển"</string> + <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Giữ và kéo để sắp xếp lại các chế độ điều khiển"</string> + <string name="controls_favorite_removed" msgid="5276978408529217272">"Đã xóa tất cả chế độ điều khiển"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Chưa lưu các thay đổi"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Xem ứng dụng khác"</string> <string name="controls_favorite_rearrange_button" msgid="2942788904364641185">"Sắp xếp lại"</string> - <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Thêm tuỳ chọn điều khiển"</string> + <string name="controls_favorite_add_controls" msgid="1221420435546694004">"Thêm chế độ điều khiển"</string> <string name="controls_favorite_back_to_editing" msgid="184125114090062713">"Quay lại chế độ chỉnh sửa"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"Không tải được các chức năng điều khiển. Hãy kiểm tra ứng dụng <xliff:g id="APP">%s</xliff:g> để đảm bảo rằng thông tin cài đặt của ứng dụng chưa thay đổi."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"Không có các chức năng điều khiển tương thích"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Không lưu được. Hãy thử lại."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Không lưu được."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Sử dụng ít nhất 4 ký tự"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Sử dụng ít hơn 16 ký tự"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số bản dựng"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số bản dựng vào bảng nhớ tạm."</string> <string name="basic_status" msgid="2315371112182658176">"Mở cuộc trò chuyện"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index cde053662150..5df113b5f5e7 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"面孔身份验证成功"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"已确认"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"点按“确认”即可完成"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"已通过面孔识别解锁。点按即可继续。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"识别出面孔。点按即可继续。"</string> @@ -721,7 +721,7 @@ <string name="left_icon" msgid="5036278531966897006">"向左图标"</string> <string name="right_icon" msgid="1103955040645237425">"向右图标"</string> <string name="drag_to_add_tiles" msgid="8933270127508303672">"按住并拖动即可添加功能块"</string> - <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住并拖动即可重新排列功能块"</string> + <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住并拖动即可重新排列图块"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖动到此处即可移除"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"您至少需要 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 个卡片"</string> <string name="qs_edit" msgid="5583565172803472437">"编辑"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"无法保存,请重试。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"无法保存。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"必须至少 4 个字符"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"必须少于 16 个字符"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build 号"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"已将 Build 号复制到剪贴板。"</string> <string name="basic_status" msgid="2315371112182658176">"开放式对话"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 1504fce96e36..f911d5c2c559 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"面孔已經驗證"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"已確認"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"輕按 [確定] 以完成"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"已使用面孔解鎖。按下即可繼續操作。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"已識別面孔。按下即可繼續操作。"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"無法儲存,請再試一次。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"無法儲存。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"請至少使用 4 個字元"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"請使用少於 16 個字元"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"版本號碼已複製到剪貼簿。"</string> <string name="basic_status" msgid="2315371112182658176">"開啟對話"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 145058805d15..01cd254a4198 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"臉孔驗證成功"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"確認完畢"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"輕觸 [確認] 完成驗證設定"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"裝置已透過你的臉解鎖,按下即可繼續操作。"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"臉孔辨識完成,按下即可繼續操作。"</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"無法儲存,請再試一次。"</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"無法儲存。"</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"至少要有 4 個半形字元"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"不得超過 16 個半形字元"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"已將版本號碼複製到剪貼簿。"</string> <string name="basic_status" msgid="2315371112182658176">"開放式對話"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 64bf21aaaf84..3f89ba951753 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -142,7 +142,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ubuso bufakazelwe ubuqiniso"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Kuqinisekisiwe"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Thepha okuthi Qinisekisa ukuze uqedele"</string> - <!-- no translation found for biometric_dialog_tap_confirm_with_face (3783056044917913453) --> + <!-- no translation found for biometric_dialog_tap_confirm_with_face (2378151312221818694) --> <skip /> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Vula ngobuso. Cindezela ukuze uqhubeke."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ubuso buyaziwa. Cindezela ukuze uqhubeke."</string> @@ -1004,7 +1004,8 @@ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Ayikwazi ukulondoloza. Zama futhi."</string> <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Ayikwazi ukulondoloza."</string> <string name="media_output_broadcast_code_hint_no_less_than_min" msgid="4663836092607696185">"Sebenzisa okungenani izinhlamvu ezi-4"</string> - <string name="media_output_broadcast_code_hint_no_more_than_max" msgid="9181869364856175638">"Sebenzisa isinhlamvu ezimbalwa kuneziyi-16"</string> + <!-- no translation found for media_output_broadcast_edit_hint_no_more_than_max (3923625800037673922) --> + <skip /> <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string> <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string> <string name="basic_status" msgid="2315371112182658176">"Vula ingxoxo"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index c3651cfa36e7..166bd2ac4439 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -902,4 +902,16 @@ <!-- Time (in ms) to delay the bouncer views from showing when passive auth may be used for device entry. --> <integer name="primary_bouncer_passive_auth_delay">500</integer> + + <!-- + The package name of the app store app. If empty, features using this should be gracefully + disabled. + --> + <string name="config_appStorePackageName" translatable="false"></string> + + <!-- Template for a link that leads to an app page in the relevant app store. If empty, + features using this should be gracefully disabled. If not empty, it must include a + "$packageName" part that will be replaced by the code with the package name of the target app. + --> + <string name="config_appStoreAppLinkTemplate" translatable="false"></string> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 8d3ba364da06..4f768cc39b40 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -572,6 +572,7 @@ <dimen name="qs_brightness_margin_bottom">16dp</dimen> <dimen name="qqs_layout_margin_top">16dp</dimen> <dimen name="qqs_layout_padding_bottom">24dp</dimen> + <item name="qqs_expand_clock_scale" format="float" type="dimen">2.57</item> <!-- Most of the time it should be the same as notification_side_paddings as it's vertically aligned with notifications. The exception is split shade when this value becomes diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 67fdb4c0c213..663efea1944b 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -353,7 +353,7 @@ <!-- Message shown when a biometric is authenticated, waiting for the user to confirm authentication [CHAR LIMIT=40]--> <string name="biometric_dialog_tap_confirm">Tap Confirm to complete</string> <!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]--> - <string name="biometric_dialog_tap_confirm_with_face">Unlocked by face.</string> + <string name="biometric_dialog_tap_confirm_with_face">Unlocked by face</string> <!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]--> <string name="biometric_dialog_tap_confirm_with_face_alt_1">Unlocked by face. Press to continue.</string> <!-- Message shown when a biometric has authenticated with a user's face and is waiting for the user to confirm authentication [CHAR LIMIT=60]--> @@ -2724,8 +2724,8 @@ <string name="media_output_broadcast_last_update_error">Can\u2019t save.</string> <!-- The hint message when Broadcast code is less than 4 characters [CHAR LIMIT=60] --> <string name="media_output_broadcast_code_hint_no_less_than_min">Use at least 4 characters</string> - <!-- The hint message when Broadcast code is more than 16 characters [CHAR LIMIT=60] --> - <string name="media_output_broadcast_code_hint_no_more_than_max">Use fewer than 16 characters</string> + <!-- The hint message when Broadcast edit is more than 16/254 characters [CHAR LIMIT=60] --> + <string name="media_output_broadcast_edit_hint_no_more_than_max">Use fewer than <xliff:g id="length" example="16">%1$d</xliff:g> characters</string> <!-- Label for clip data when copying the build number off QS [CHAR LIMIT=NONE]--> <string name="build_number_clip_data_label">Build number</string> @@ -3016,52 +3016,54 @@ --> <string name="keyguard_affordance_enablement_dialog_action_template">Open <xliff:g id="appName" example="Wallet">%1$s</xliff:g></string> - <!-- - Requirement for the wallet app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + <!--- + Explains that the wallet app is not available because it is not installed. This is shown as part + of a dialog that explains to the user why they cannot select this shortcut for their lock screen + right now. + [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1">• The app is set up</string> + <string name="wallet_quick_affordance_unavailable_install_the_app">To add the Wallet app as a shortcut, make sure the app is installed</string> - <!-- - Requirement for the wallet app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + <!--- + Explains that the wallet app is not available because it is not installed. This is shown as part + of a dialog that explains to the user why they cannot select this shortcut for their lock screen + right now. + [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2">• At least one card has been added to Wallet</string> + <string name="wallet_quick_affordance_unavailable_configure_the_app">To add the Wallet app as a shortcut, make sure at least one card has been added</string> <!-- Requirement for the QR code scanner functionality to be available for the user to use. This is shown as part of a bulleted list of requirements. When all requirements are met, the piece of functionality can be accessed through a shortcut button on the lock screen. [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction">• Install a camera app</string> + <string name="qr_scanner_quick_affordance_unavailable_explanation">To add the QR code scanner as a shortcut, make sure a camera app is installed</string> <!-- - Requirement for the home app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + Explains that the lock screen shortcut for the "home" app is not available because the app isn't + installed. This is shown as part of a dialog that explains to the user why they cannot select + this shortcut for their lock screen right now. [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_home_instruction_1">• The app is set up</string> + <string name="home_quick_affordance_unavailable_install_the_app">To add the Home app as a shortcut, make sure the app is installed</string> <!-- - Requirement for the home app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE]. + Explains that the lock screen shortcut for the "home" app is not available because the app isn't + configured. This is shown as part of a dialog that explains to the user why they cannot select + this shortcut for their lock screen right now. [CHAR LIMIT=NONE]. --> - <string name="keyguard_affordance_enablement_dialog_home_instruction_2">• At least one device is available</string> + <string name="home_quick_affordance_unavailable_configure_the_app">• At least one device is available</string> <!--- - Requirement for the notes app to be available for the user to use. This is shown as part of a - bulleted list of requirements. When all requirements are met, the app can be accessed through a - shortcut button on the lock screen. [CHAR LIMIT=NONE] --> - <string name="keyguard_affordance_enablement_dialog_notes_app_instruction">Select a default notes app to use the notetaking shortcut</string> + Explains that the notes app is not available. This is shown as part of a dialog that explains to + the user why they cannot select this shortcut for their lock screen right now. + [CHAR LIMIT=NONE]. + --> + <string name="notes_app_quick_affordance_unavailable_explanation">Select a default notes app to use the notetaking shortcut</string> <!--- The action to make the lock screen shortcut for the notes app to be available for the user to - use. This is shown as the action button in the dialog listing the requirements. When all - requirements are met, the app can be accessed through a shortcut button on the lock screen. - [CHAR LIMIT=NONE] --> + use. This is shown as the action button in the dialog explaining why the shortcut isn't + available. [CHAR LIMIT=NONE] --> <string name="keyguard_affordance_enablement_dialog_notes_app_action">Select app</string> <!-- @@ -3153,4 +3155,11 @@ <!--- Content of toast triggered when the notes app entry point is triggered without setting a default notes app. [CHAR LIMIT=NONE] --> <string name="set_default_notes_app_toast_content">Set default notes app in Settings</string> + + <!-- + Label for a button that, when clicked, sends the user to the app store to install an app. + + [CHAR LIMIT=64]. + --> + <string name="install_app">Install app</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index cee21353bd68..cb5342a0d66b 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1183,12 +1183,6 @@ <style name="TextAppearance.InternetDialog.Secondary.Active"/> - <style name="InternetDialog.Divider"> - <item name="android:background">?android:attr/textColorSecondary</item> - </style> - - <style name="InternetDialog.Divider.Active"/> - <style name="FgsManagerDialogTitle"> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> <item name="android:textStyle">bold</item> diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml index 52a98984e6e2..8039c68485ca 100644 --- a/packages/SystemUI/res/xml/qs_header.xml +++ b/packages/SystemUI/res/xml/qs_header.xml @@ -43,8 +43,8 @@ app:layout_constraintBottom_toBottomOf="@id/carrier_group" /> <Transform - android:scaleX="2.57" - android:scaleY="2.57" + android:scaleX="@dimen/qqs_expand_clock_scale" + android:scaleY="@dimen/qqs_expand_clock_scale" /> </Constraint> diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt index f96d1e3d7fef..070a45170d04 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt @@ -62,6 +62,15 @@ open class ViewScreenshotTestRule( private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false override fun apply(base: Statement, description: Description): Statement { + if (isRobolectric) { + // In robolectric mode, we enable NATIVE graphics and unpack font and icu files. + // We need to use reflection, as this library is only needed and therefore + // only available in deviceless mode. + val nativeLoaderClassName = "org.robolectric.nativeruntime.DefaultNativeRuntimeLoader" + val defaultNativeRuntimeLoader = Class.forName(nativeLoaderClassName) + System.setProperty("robolectric.graphicsMode", "NATIVE") + defaultNativeRuntimeLoader.getMethod("injectAndLoad").invoke(null) + } val ruleToApply = if (isRobolectric) roboRule else delegateRule return ruleToApply.apply(base, description) } @@ -69,6 +78,7 @@ open class ViewScreenshotTestRule( protected fun takeScreenshot( mode: Mode = Mode.WrapContent, viewProvider: (ComponentActivity) -> View, + beforeScreenshot: (ComponentActivity) -> Unit = {} ): Bitmap { activityRule.scenario.onActivity { activity -> // Make sure that the activity draws full screen and fits the whole display instead of @@ -94,6 +104,7 @@ open class ViewScreenshotTestRule( val content = activity.requireViewById<ViewGroup>(android.R.id.content) assertEquals(1, content.childCount) contentView = content.getChildAt(0) + beforeScreenshot(activity) } return if (isRobolectric) { @@ -111,9 +122,10 @@ open class ViewScreenshotTestRule( fun screenshotTest( goldenIdentifier: String, mode: Mode = Mode.WrapContent, - viewProvider: (ComponentActivity) -> View, + beforeScreenshot: (ComponentActivity) -> Unit = {}, + viewProvider: (ComponentActivity) -> View ) { - val bitmap = takeScreenshot(mode, viewProvider) + val bitmap = takeScreenshot(mode, viewProvider, beforeScreenshot) screenshotRule.assertBitmapAgainstGolden( bitmap, goldenIdentifier, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index c5979cc50c3c..7a8c82cee32a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -65,8 +65,8 @@ class UnfoldConstantTranslateAnimator( } else { 1 } - viewsToTranslate.forEach { (view, direction) -> - view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier + viewsToTranslate.forEach { (view, direction, func) -> + view.get()?.let { func(it, xTrans * direction.multiplier * rtlMultiplier) } } } @@ -77,7 +77,7 @@ class UnfoldConstantTranslateAnimator( .filter { it.shouldBeAnimated() } .mapNotNull { parent.findViewById<View>(it.viewId)?.let { view -> - ViewToTranslate(WeakReference(view), it.direction) + ViewToTranslate(WeakReference(view), it.direction, it.translateFunc) } } .toList() @@ -91,14 +91,19 @@ class UnfoldConstantTranslateAnimator( data class ViewIdToTranslate( val viewId: Int, val direction: Direction, - val shouldBeAnimated: () -> Boolean = { true } + val shouldBeAnimated: () -> Boolean = { true }, + val translateFunc: (View, Float) -> Unit = { view, value -> view.translationX = value }, ) /** * Represents a view whose animation process is in-progress. It should be immutable because the * started animation should be completed. */ - private data class ViewToTranslate(val view: WeakReference<View>, val direction: Direction) + private data class ViewToTranslate( + val view: WeakReference<View>, + val direction: Direction, + val translateFunc: (View, Float) -> Unit, + ) /** Direction of the animation. */ enum class Direction(val multiplier: Float) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java index 9a9a2426507f..6b67c092890d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java @@ -79,17 +79,26 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager /** Alerts listener and plugin that the plugin has been created. */ public void onCreate() { - mListener.onPluginAttached(this); + boolean loadPlugin = mListener.onPluginAttached(this); + if (!loadPlugin) { + if (mPlugin != null) { + unloadPlugin(); + } + return; + } + if (mPlugin == null) { loadPlugin(); - } else { - if (!(mPlugin instanceof PluginFragment)) { - // Only call onCreate for plugins that aren't fragments, as fragments - // will get the onCreate as part of the fragment lifecycle. - mPlugin.onCreate(mAppContext, mPluginContext); - } - mListener.onPluginLoaded(mPlugin, mPluginContext, this); + return; } + + mPluginFactory.checkVersion(mPlugin); + if (!(mPlugin instanceof PluginFragment)) { + // Only call onCreate for plugins that aren't fragments, as fragments + // will get the onCreate as part of the fragment lifecycle. + mPlugin.onCreate(mAppContext, mPluginContext); + } + mListener.onPluginLoaded(mPlugin, mPluginContext, this); } /** Alerts listener and plugin that the plugin is being shutdown. */ @@ -118,6 +127,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return; } + mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. @@ -205,12 +215,8 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager PluginFactory<T> pluginFactory = new PluginFactory<T>( context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass, () -> getClassLoader(appInfo, mBaseClassLoader)); - // TODO: Only create the plugin before version check if we need it for - // legacy version check. - T instance = pluginFactory.createPlugin(); - pluginFactory.checkVersion(instance); return new PluginInstance<T>( - context, listener, componentName, pluginFactory, instance); + context, listener, componentName, pluginFactory, null); } private boolean isPluginPackagePrivileged(String packageName) { @@ -332,7 +338,9 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager ClassLoader loader = mClassLoaderFactory.get(); Class<T> instanceClass = (Class<T>) Class.forName( mComponentName.getClassName(), true, loader); - return (T) mInstanceFactory.create(instanceClass); + T result = (T) mInstanceFactory.create(instanceClass); + Log.v(TAG, "Created plugin: " + result); + return result; } catch (ClassNotFoundException ex) { Log.e(TAG, "Failed to load plugin", ex); } catch (IllegalAccessException ex) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt new file mode 100644 index 000000000000..495d3a13d961 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProvider.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.android.systemui.unfold.util + +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback +import java.util.concurrent.Executor + +/** + * [UnfoldTransitionProgressProvider] that emits transition progress only when unfolding but not + * when folding, so we can play the animation only one way but not the other way. + */ +class UnfoldOnlyProgressProvider( + foldProvider: FoldProvider, + @Main private val executor: Executor, + private val sourceProvider: UnfoldTransitionProgressProvider, + private val scopedProvider: ScopedUnfoldTransitionProgressProvider = + ScopedUnfoldTransitionProgressProvider(sourceProvider) +) : UnfoldTransitionProgressProvider by scopedProvider { + + private var isFolded = false + + init { + foldProvider.registerCallback(FoldListener(), executor) + sourceProvider.addCallback(SourceTransitionListener()) + } + + private inner class SourceTransitionListener : TransitionProgressListener { + override fun onTransitionFinished() { + // Disable scoped progress provider after the first unfold animation, so fold animation + // will not be propagated. It will be re-enabled after folding so we can play + // the unfold animation again. + if (!isFolded) { + scopedProvider.setReadyToHandleTransition(false) + } + } + } + + private inner class FoldListener : FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + if (isFolded) { + scopedProvider.setReadyToHandleTransition(true) + } + + this@UnfoldOnlyProgressProvider.isFolded = isFolded + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index d9d64ad5a893..376e27c6cf44 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -1,8 +1,14 @@ package com.android.keyguard; import static android.view.View.ALPHA; +import static android.view.View.SCALE_X; +import static android.view.View.SCALE_Y; import static android.view.View.TRANSLATION_Y; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN; +import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -17,6 +23,7 @@ import android.widget.RelativeLayout; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; +import androidx.core.content.res.ResourcesCompat; import com.android.app.animation.Interpolators; import com.android.keyguard.dagger.KeyguardStatusViewScope; @@ -44,6 +51,7 @@ public class KeyguardClockSwitch extends RelativeLayout { private static final long STATUS_AREA_START_DELAY_MILLIS = 0; private static final long STATUS_AREA_MOVE_UP_MILLIS = 967; private static final long STATUS_AREA_MOVE_DOWN_MILLIS = 467; + private static final float SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER = 1.4f; @IntDef({LARGE, SMALL}) @Retention(RetentionPolicy.SOURCE) @@ -88,14 +96,18 @@ public class KeyguardClockSwitch extends RelativeLayout { private KeyguardClockFrame mLargeClockFrame; private ClockController mClock; - private View mStatusArea; + private KeyguardStatusAreaView mStatusArea; private int mSmartspaceTopOffset; + private float mWeatherClockSmartspaceScaling = 1f; + private int mWeatherClockSmartspaceTranslateX = 0; + private int mWeatherClockSmartspaceTranslateY = 0; private int mDrawAlpha = 255; /** * Maintain state so that a newly connected plugin can be initialized. */ private float mDarkAmount; + private boolean mSplitShadeCentered = false; /** * Indicates which clock is currently displayed - should be one of {@link ClockSize}. @@ -105,7 +117,7 @@ public class KeyguardClockSwitch extends RelativeLayout { @VisibleForTesting AnimatorSet mClockInAnim = null; @VisibleForTesting AnimatorSet mClockOutAnim = null; - private AnimatorSet mStatusAreaAnim = null; + @VisibleForTesting AnimatorSet mStatusAreaAnim = null; private int mClockSwitchYAmount; @VisibleForTesting boolean mChildrenAreLaidOut = false; @@ -117,13 +129,30 @@ public class KeyguardClockSwitch extends RelativeLayout { } /** - * Apply dp changes on font/scale change + * Apply dp changes on configuration change */ - public void onDensityOrFontScaleChanged() { + public void onConfigChanged() { mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_clock_switch_y_shift); mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_smartspace_top_offset); + mWeatherClockSmartspaceScaling = ResourcesCompat.getFloat( + mContext.getResources(), R.dimen.weather_clock_smartspace_scale); + mWeatherClockSmartspaceTranslateX = mContext.getResources().getDimensionPixelSize( + R.dimen.weather_clock_smartspace_translateX); + mWeatherClockSmartspaceTranslateY = mContext.getResources().getDimensionPixelSize( + R.dimen.weather_clock_smartspace_translateY); + updateStatusArea(/* animate= */false); + } + + /** + * Enable or disable split shade specific positioning + */ + public void setSplitShadeCentered(boolean splitShadeCentered) { + if (mSplitShadeCentered != splitShadeCentered) { + mSplitShadeCentered = splitShadeCentered; + updateStatusArea(/* animate= */true); + } } @Override @@ -134,7 +163,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large); mStatusArea = findViewById(R.id.keyguard_status_area); - onDensityOrFontScaleChanged(); + onConfigChanged(); } @Override @@ -182,6 +211,13 @@ public class KeyguardClockSwitch extends RelativeLayout { mSmallClockFrame.addView(clock.getSmallClock().getView()); mLargeClockFrame.addView(clock.getLargeClock().getView()); updateClockTargetRegions(); + updateStatusArea(/* animate= */false); + } + + private void updateStatusArea(boolean animate) { + if (mDisplayedClockSize != null && mChildrenAreLaidOut) { + updateClockViews(mDisplayedClockSize == LARGE, animate); + } } void updateClockTargetRegions() { @@ -230,13 +266,25 @@ public class KeyguardClockSwitch extends RelativeLayout { mStatusAreaAnim = null; View in, out; - float statusAreaYTranslation, clockInYTranslation, clockOutYTranslation; + float statusAreaYTranslation, statusAreaClockScale = 1f; + float statusAreaClockTranslateX = 0f, statusAreaClockTranslateY = 0f; + float clockInYTranslation, clockOutYTranslation; if (useLargeClock) { out = mSmallClockFrame; in = mLargeClockFrame; if (indexOfChild(in) == -1) addView(in, 0); statusAreaYTranslation = mSmallClockFrame.getTop() - mStatusArea.getTop() + mSmartspaceTopOffset; + // TODO: Load from clock config when less risky + if (mClock != null + && mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) { + statusAreaClockScale = mWeatherClockSmartspaceScaling; + statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX; + statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY; + if (mSplitShadeCentered) { + statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER; + } + } clockInYTranslation = 0; clockOutYTranslation = 0; // Small clock translation is handled with statusArea } else { @@ -258,7 +306,12 @@ public class KeyguardClockSwitch extends RelativeLayout { in.setAlpha(1f); in.setTranslationY(clockInYTranslation); in.setVisibility(View.VISIBLE); - mStatusArea.setTranslationY(statusAreaYTranslation); + mStatusArea.setScaleX(statusAreaClockScale); + mStatusArea.setScaleY(statusAreaClockScale); + mStatusArea.setTranslateXFromClockDesign(statusAreaClockTranslateX); + mStatusArea.setTranslateYFromClockDesign(statusAreaClockTranslateY); + mStatusArea.setTranslateYFromClockSize(statusAreaYTranslation); + mSmallClockFrame.setTranslationY(statusAreaYTranslation); return; } @@ -295,8 +348,15 @@ public class KeyguardClockSwitch extends RelativeLayout { useLargeClock ? STATUS_AREA_MOVE_UP_MILLIS : STATUS_AREA_MOVE_DOWN_MILLIS); mStatusAreaAnim.setInterpolator(Interpolators.EMPHASIZED); mStatusAreaAnim.playTogether( - ObjectAnimator.ofFloat(mStatusArea, TRANSLATION_Y, statusAreaYTranslation), - ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation)); + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_SIZE.getProperty(), + statusAreaYTranslation), + ObjectAnimator.ofFloat(mSmallClockFrame, TRANSLATION_Y, statusAreaYTranslation), + ObjectAnimator.ofFloat(mStatusArea, SCALE_X, statusAreaClockScale), + ObjectAnimator.ofFloat(mStatusArea, SCALE_Y, statusAreaClockScale), + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_X_CLOCK_DESIGN.getProperty(), + statusAreaClockTranslateX), + ObjectAnimator.ofFloat(mStatusArea, TRANSLATE_Y_CLOCK_DESIGN.getProperty(), + statusAreaClockTranslateY)); mStatusAreaAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { mStatusAreaAnim = null; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 99e25745dda7..41c1eda42e83 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -330,10 +330,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } /** - * Apply dp changes on font/scale change + * Apply dp changes on configuration change */ - public void onDensityOrFontScaleChanged() { - mView.onDensityOrFontScaleChanged(); + public void onConfigChanged() { + mView.onConfigChanged(); mKeyguardSmallClockTopMargin = mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin); mKeyguardLargeClockTopMargin = @@ -344,6 +344,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS setDateWeatherVisibility(); } + /** + * Enable or disable split shade center specific positioning + */ + public void setSplitShadeCentered(boolean splitShadeCentered) { + mView.setSplitShadeCentered(splitShadeCentered); + } /** * Set which clock should be displayed on the keyguard. The other one will be automatically @@ -407,7 +413,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS scale, props, animate); if (mStatusArea != null) { - PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X, + PropertyAnimator.setProperty(mStatusArea, KeyguardStatusAreaView.TRANSLATE_X_AOD, x, props, animate); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index 0332c9f57136..363dd014beb6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -208,7 +208,7 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea> @Override public void run() { final View host = mHost.get(); - if (host != null) { + if (host != null && host.isVisibleToUser()) { host.announceForAccessibility(mTextToAnnounce); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 4b021716f506..4211f55787d9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -1226,15 +1226,16 @@ public class KeyguardSecurityContainer extends ConstraintLayout { constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT); constraintSet.applyTo(mView); } else { - int leftElement = leftAlign ? mViewFlipper.getId() : mUserSwitcherViewGroup.getId(); - int rightElement = + int startElement = + leftAlign ? mViewFlipper.getId() : mUserSwitcherViewGroup.getId(); + int endElement = leftAlign ? mUserSwitcherViewGroup.getId() : mViewFlipper.getId(); ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.connect(leftElement, LEFT, PARENT_ID, LEFT); - constraintSet.connect(leftElement, RIGHT, rightElement, LEFT); - constraintSet.connect(rightElement, LEFT, leftElement, RIGHT); - constraintSet.connect(rightElement, RIGHT, PARENT_ID, RIGHT); + constraintSet.connect(startElement, START, PARENT_ID, START); + constraintSet.connect(startElement, END, endElement, START); + constraintSet.connect(endElement, START, startElement, END); + constraintSet.connect(endElement, END, PARENT_ID, END); constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP); constraintSet.connect(mUserSwitcherViewGroup.getId(), BOTTOM, PARENT_ID, BOTTOM); constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index b5e54209dab2..74b29a77aad4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -264,13 +264,6 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard */ @Override public void finish(boolean fromPrimaryAuth, int targetUserId) { - if (!mKeyguardStateController.canDismissLockScreen() && !fromPrimaryAuth) { - Log.e(TAG, - "Tried to dismiss keyguard when lockscreen is not dismissible and user " - + "was not authenticated with a primary security method " - + "(pin/password/pattern)."); - return; - } // If there's a pending runnable because the user interacted with a widget // and we're leaving keyguard, then run it. boolean deferKeyguardDone = false; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt new file mode 100644 index 000000000000..e7da2b977379 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusAreaView.kt @@ -0,0 +1,118 @@ +package com.android.keyguard + +import android.content.Context +import android.util.AttributeSet +import android.util.FloatProperty +import android.widget.LinearLayout +import com.android.systemui.R +import com.android.systemui.statusbar.notification.AnimatableProperty + +class KeyguardStatusAreaView( + context: Context, + attrs: AttributeSet? = null, +) : LinearLayout(context, attrs) { + var translateXFromClockDesign = 0f + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateXFromAod = 0f + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateXFromUnfold = 0F + get() = field + set(value) { + field = value + translationX = translateXFromAod + translateXFromClockDesign + translateXFromUnfold + } + + var translateYFromClockSize = 0f + get() = field + set(value) { + field = value + translationY = value + translateYFromClockDesign + } + + var translateYFromClockDesign = 0f + get() = field + set(value) { + field = value + translationY = value + translateYFromClockSize + } + + companion object { + @JvmField + val TRANSLATE_X_CLOCK_DESIGN = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateXClockDesign") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateXFromClockDesign = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateXFromClockDesign + } + }, + R.id.translate_x_clock_design_animator_tag, + R.id.translate_x_clock_design_animator_start_tag, + R.id.translate_x_clock_design_animator_end_tag + ) + + @JvmField + val TRANSLATE_X_AOD = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateXAod") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateXFromAod = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateXFromAod + } + }, + R.id.translate_x_aod_animator_tag, + R.id.translate_x_aod_animator_start_tag, + R.id.translate_x_aod_animator_end_tag + ) + + @JvmField + val TRANSLATE_Y_CLOCK_SIZE = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockSize") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateYFromClockSize = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateYFromClockSize + } + }, + R.id.translate_y_clock_size_animator_tag, + R.id.translate_y_clock_size_animator_start_tag, + R.id.translate_y_clock_size_animator_end_tag + ) + + @JvmField + val TRANSLATE_Y_CLOCK_DESIGN = + AnimatableProperty.from( + object : FloatProperty<KeyguardStatusAreaView>("TranslateYClockDesign") { + override fun setValue(view: KeyguardStatusAreaView, value: Float) { + view.translateYFromClockDesign = value + } + + override fun get(view: KeyguardStatusAreaView): Float { + return view.translateYFromClockDesign + } + }, + R.id.translate_y_clock_design_animator_tag, + R.id.translate_y_clock_design_animator_start_tag, + R.id.translate_y_clock_design_animator_end_tag + ) + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 835cc13bebb1..00500d617766 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -24,6 +24,7 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CL import android.animation.Animator; import android.animation.ValueAnimator; import android.annotation.Nullable; +import android.content.res.Configuration; import android.graphics.Rect; import android.transition.ChangeBounds; import android.transition.Transition; @@ -280,8 +281,8 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } @Override - public void onDensityOrFontScaleChanged() { - mKeyguardClockSwitchController.onDensityOrFontScaleChanged(); + public void onConfigChanged(Configuration newConfig) { + mKeyguardClockSwitchController.onConfigChanged(); } }; @@ -329,6 +330,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV boolean splitShadeEnabled, boolean shouldBeCentered, boolean animate) { + mKeyguardClockSwitchController.setSplitShadeCentered(splitShadeEnabled && shouldBeCentered); if (mStatusViewCentered == shouldBeCentered) { return; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index edd150c293f6..ca64ae059093 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -53,7 +53,10 @@ constructor( UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard), + ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard, + { view, value -> + (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value + }), ViewIdToTranslate( R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly), ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard), diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 1721891550a1..2bf8fb36469c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -3565,7 +3565,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ private void handleTimeUpdate() { Assert.isMainThread(); - mLogger.d("handleTimeUpdate"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -3630,9 +3629,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void handleBatteryUpdate(BatteryStatus status) { Assert.isMainThread(); final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status); - mLogger.logHandleBatteryUpdate(batteryUpdateInteresting); mBatteryStatus = status; if (batteryUpdateInteresting) { + mLogger.logHandleBatteryUpdate(mBatteryStatus); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -3669,7 +3668,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.logSimState(subId, slotId, state); boolean becameAbsent = false; - if (!SubscriptionManager.isValidSubscriptionId(subId)) { + if (!SubscriptionManager.isValidSubscriptionId(subId) + && state != TelephonyManager.SIM_STATE_UNKNOWN) { mLogger.w("invalid subId in handleSimStateChange()"); /* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to * handleServiceStateChange() handle other case */ @@ -3704,7 +3704,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab data.subId = subId; data.slotId = slotId; } - if ((changed || becameAbsent) && state != TelephonyManager.SIM_STATE_UNKNOWN) { + if ((changed || becameAbsent) || state == TelephonyManager.SIM_STATE_UNKNOWN) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 7cedecc33a02..239a0cc01c45 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -326,7 +326,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } if (!Objects.equals(prevContentDescription, mView.getContentDescription()) - && mView.getContentDescription() != null) { + && mView.getContentDescription() != null && mView.isVisibleToUser()) { mView.announceForAccessibility(mView.getContentDescription()); } } diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java index 4557b3418e19..500910c910c3 100644 --- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java +++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java @@ -30,6 +30,7 @@ import android.transition.TransitionManager; import android.transition.TransitionValues; import android.util.AttributeSet; import android.util.Log; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -51,12 +52,30 @@ public class PinShapeNonHintingView extends LinearLayout implements PinShapeInpu private int mPosition = 0; private final PinShapeAdapter mPinShapeAdapter; private ValueAnimator mValueAnimator = ValueAnimator.ofFloat(1f, 0f); + private Rect mFirstChildVisibleRect = new Rect(); public PinShapeNonHintingView(Context context, AttributeSet attrs) { super(context, attrs); mPinShapeAdapter = new PinShapeAdapter(context); } @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + if (getChildCount() > 0) { + View firstChild = getChildAt(0); + boolean isVisible = firstChild.getLocalVisibleRect(mFirstChildVisibleRect); + boolean clipped = mFirstChildVisibleRect.left > 0 + || mFirstChildVisibleRect.right < firstChild.getWidth(); + if (!isVisible || clipped) { + setGravity(Gravity.END | Gravity.CENTER_VERTICAL); + return; + } + } + + setGravity(Gravity.CENTER); + } + + @Override public void append() { int size = getResources().getDimensionPixelSize(R.dimen.password_shape_size); ImageView pinDot = new ImageView(getContext()); diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index 4923ab0fab18..b5963312cb2d 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -30,7 +30,7 @@ import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.KeyguardListenModel import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.keyguard.TrustGrantFlags -import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog +import com.android.settingslib.fuelgauge.BatteryStatus import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel import com.android.systemui.log.LogLevel.DEBUG @@ -38,6 +38,7 @@ import com.android.systemui.log.LogLevel.ERROR import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.LogLevel.VERBOSE import com.android.systemui.log.LogLevel.WARNING +import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject @@ -683,8 +684,27 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) { ) } - fun logHandleBatteryUpdate(isInteresting: Boolean) { - logBuffer.log(TAG, DEBUG, { bool1 = isInteresting }, { "handleBatteryUpdate: $bool1" }) + fun logHandleBatteryUpdate(batteryStatus: BatteryStatus?) { + logBuffer.log( + TAG, + DEBUG, + { + bool1 = batteryStatus != null + int1 = batteryStatus?.status ?: -1 + int2 = batteryStatus?.chargingStatus ?: -1 + long1 = (batteryStatus?.level ?: -1).toLong() + long2 = (batteryStatus?.maxChargingWattage ?: -1).toLong() + str1 = "${batteryStatus?.plugged ?: -1}" + }, + { + "handleBatteryUpdate: isNotNull: $bool1 " + + "BatteryStatus{status= $int1, " + + "level=$long1, " + + "plugged=$str1, " + + "chargingStatus=$int2, " + + "maxChargingWattage= $long2}" + } + ) } fun scheduleWatchdog(@CompileTimeConstant watchdogType: String) { diff --git a/packages/SystemUI/src/com/android/systemui/DejankUtils.java b/packages/SystemUI/src/com/android/systemui/DejankUtils.java index 3fce55f6e515..146d12c3e621 100644 --- a/packages/SystemUI/src/com/android/systemui/DejankUtils.java +++ b/packages/SystemUI/src/com/android/systemui/DejankUtils.java @@ -29,13 +29,17 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.StrictMode; import android.os.SystemProperties; +import android.os.Trace; import android.view.Choreographer; +import android.view.View; +import android.view.ViewRootImpl; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.util.Assert; import java.util.ArrayList; import java.util.HashSet; +import java.util.Random; import java.util.Stack; import java.util.function.Supplier; @@ -43,12 +47,14 @@ import java.util.function.Supplier; * Utility class for methods used to dejank the UI. */ public class DejankUtils { + private static final String TRACK_NAME = "DejankUtils"; public static final boolean STRICT_MODE_ENABLED = Build.IS_ENG || SystemProperties.getBoolean("persist.sysui.strictmode", false); private static final Choreographer sChoreographer = Choreographer.getInstance(); private static final Handler sHandler = new Handler(); private static final ArrayList<Runnable> sPendingRunnables = new ArrayList<>(); + private static final Random sRandom = new Random(); private static Stack<String> sBlockingIpcs = new Stack<>(); private static boolean sTemporarilyIgnoreStrictMode; private static final HashSet<String> sWhitelistedFrameworkClasses = new HashSet<>(); @@ -254,4 +260,30 @@ public class DejankUtils { public static void setImmediate(boolean immediate) { sImmediate = immediate; } + + /** + * Calls notifyRendererOfExpensiveFrame on the ViewRootImpl after performing null checks. + */ + public static void notifyRendererOfExpensiveFrame(View view, String reason) { + if (view == null) return; + notifyRendererOfExpensiveFrame(view.getViewRootImpl(), reason); + } + + /** + * Calls notifyRendererOfExpensiveFrame on the ViewRootImpl after performing null checks. + */ + public static void notifyRendererOfExpensiveFrame(ViewRootImpl viewRoot, String reason) { + if (viewRoot == null) return; + if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) { + int cookie = sRandom.nextInt(); + Trace.asyncTraceForTrackBegin( + Trace.TRACE_TAG_APP, + TRACK_NAME, + "notifyRendererOfExpensiveFrame (" + reason + ")", + cookie); + DejankUtils.postAfterTraversal( + () -> Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, cookie)); + } + viewRoot.notifyRendererOfExpensiveFrame(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 99d46629ad7a..806d1aff3a30 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -20,6 +20,7 @@ import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; +import static com.android.systemui.statusbar.notification.NotificationUtils.logKey; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -33,6 +34,7 @@ import android.app.PendingIntent; import android.content.res.Resources; import android.graphics.RectF; import android.os.Handler; +import android.os.Trace; import android.util.ArrayMap; import android.util.Log; import android.view.MotionEvent; @@ -54,9 +56,10 @@ import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.animation.PhysicsAnimator.SpringConfig; +import java.io.PrintWriter; import java.util.function.Consumer; -public class SwipeHelper implements Gefingerpoken { +public class SwipeHelper implements Gefingerpoken, Dumpable { static final String TAG = "com.android.systemui.SwipeHelper"; private static final boolean DEBUG_INVALIDATE = false; private static final boolean CONSTRAIN_SWIPE = true; @@ -275,9 +278,11 @@ public class SwipeHelper implements Gefingerpoken { // invalidate the view's own bounds all the way up the view hierarchy public static void invalidateGlobalRegion(View view) { + Trace.beginSection("SwipeHelper.invalidateGlobalRegion"); invalidateGlobalRegion( view, new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); + Trace.endSection(); } // invalidate a rectangle relative to the view's coordinate system all the way up the view @@ -490,7 +495,7 @@ public class SwipeHelper implements Gefingerpoken { } if (!mCancelled || wasRemoved) { mCallback.onChildDismissed(animView); - resetSwipeOfView(animView); + resetViewIfSwiping(animView); } if (endAction != null) { endAction.accept(mCancelled); @@ -545,7 +550,11 @@ public class SwipeHelper implements Gefingerpoken { if (!cancelled) { updateSwipeProgressFromOffset(animView, canBeDismissed); - resetSwipeOfView(animView); + resetViewIfSwiping(animView); + // Clear the snapped view after success, assuming it's not being swiped now + if (animView == mTouchedView && !mIsSwiping) { + mTouchedView = null; + } } onChildSnappedBack(animView, targetLeft); }); @@ -805,19 +814,51 @@ public class SwipeHelper implements Gefingerpoken { return mIsSwiping ? mTouchedView : null; } - protected void resetSwipeOfView(View view) { + protected void resetViewIfSwiping(View view) { if (getSwipedView() == view) { resetSwipeState(); } } - public void resetSwipeState() { - View swipedView = getSwipedView(); + private void resetSwipeState() { + resetSwipeStates(/* resetAll= */ false); + } + + public void resetTouchState() { + resetSwipeStates(/* resetAll= */ true); + } + + public void forceResetSwipeState(@NonNull View view) { + if (view.getTranslationX() == 0) return; + setTranslation(view, 0); + updateSwipeProgressFromOffset(view, /* dismissable= */ true, 0); + } + + /** This method resets the swipe state, and if `resetAll` is true, also resets the snap state */ + private void resetSwipeStates(boolean resetAll) { + final View touchedView = mTouchedView; + final boolean wasSnapping = mSnappingChild; + final boolean wasSwiping = mIsSwiping; mTouchedView = null; mIsSwiping = false; - if (swipedView != null) { - snapChildIfNeeded(swipedView, false, 0); - onChildSnappedBack(swipedView, 0); + // If we were swiping, then we resetting swipe requires resetting everything. + resetAll |= wasSwiping; + if (resetAll) { + mSnappingChild = false; + } + if (touchedView == null) return; // No view to reset visually + // When snap needs to be reset, first thing is to cancel any translation animation + final boolean snapNeedsReset = resetAll && wasSnapping; + if (snapNeedsReset) { + cancelTranslateAnimation(touchedView); + } + // actually reset the view to default state + if (resetAll) { + snapChildIfNeeded(touchedView, false, 0); + } + // report if a swipe or snap was reset. + if (wasSwiping || snapNeedsReset) { + onChildSnappedBack(touchedView, 0); } } @@ -844,6 +885,31 @@ public class SwipeHelper implements Gefingerpoken { return false; } + @Override + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.append("mTouchedView=").print(mTouchedView); + if (mTouchedView instanceof ExpandableNotificationRow) { + pw.append(" key=").println(logKey((ExpandableNotificationRow) mTouchedView)); + } else { + pw.println(); + } + pw.append("mIsSwiping=").println(mIsSwiping); + pw.append("mSnappingChild=").println(mSnappingChild); + pw.append("mLongPressSent=").println(mLongPressSent); + pw.append("mInitialTouchPos=").println(mInitialTouchPos); + pw.append("mTranslation=").println(mTranslation); + pw.append("mCanCurrViewBeDimissed=").println(mCanCurrViewBeDimissed); + pw.append("mMenuRowIntercepting=").println(mMenuRowIntercepting); + pw.append("mDisableHwLayers=").println(mDisableHwLayers); + pw.append("mDismissPendingMap: ").println(mDismissPendingMap.size()); + if (!mDismissPendingMap.isEmpty()) { + mDismissPendingMap.forEach((view, animator) -> { + pw.append(" ").print(view); + pw.append(": ").println(animator); + }); + } + } + public interface Callback { View getChildAtPosition(MotionEvent ev); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt index 682888fc39b5..95610aec3562 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt @@ -21,6 +21,7 @@ import android.content.Context import com.airbnb.lottie.LottieAnimationView import com.android.systemui.R import com.android.systemui.biometrics.AuthBiometricView.BiometricState +import com.android.systemui.biometrics.AuthBiometricView.STATE_AUTHENTICATED import com.android.systemui.biometrics.AuthBiometricView.STATE_ERROR import com.android.systemui.biometrics.AuthBiometricView.STATE_HELP import com.android.systemui.biometrics.AuthBiometricView.STATE_PENDING_CONFIRMATION @@ -47,13 +48,16 @@ open class AuthBiometricFingerprintAndFaceIconController( @BiometricState oldState: Int, @BiometricState newState: Int ): Int? = when (newState) { + STATE_AUTHENTICATED -> { + if (oldState == STATE_PENDING_CONFIRMATION) { + R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie + } else { + super.getAnimationForTransition(oldState, newState) + } + } STATE_PENDING_CONFIRMATION -> { if (oldState == STATE_ERROR || oldState == STATE_HELP) { R.raw.fingerprint_dialogue_error_to_unlock_lottie - } else if (oldState == STATE_PENDING_CONFIRMATION) { - // TODO(jbolinger): missing asset for this transition - // (unlocked icon to success checkmark) - R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie } else { R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index fb160f2a2f00..cd8f04d18500 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -109,6 +109,7 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom int ACTION_ERROR = 5; int ACTION_USE_DEVICE_CREDENTIAL = 6; int ACTION_START_DELAYED_FINGERPRINT_SENSOR = 7; + int ACTION_AUTHENTICATED_AND_CONFIRMED = 8; /** * When an action has occurred. The caller will only invoke this when the callback should @@ -509,7 +510,8 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom } public void updateState(@BiometricState int newState) { - Log.v(TAG, "newState: " + newState); + Log.d(TAG, "newState: " + newState); + mIconController.updateState(mState, newState); switch (newState) { @@ -533,8 +535,14 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom } announceForAccessibility(getResources() .getString(R.string.biometric_dialog_authenticated)); - mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED), - getDelayAfterAuthenticatedDurationMs()); + if (mState == STATE_PENDING_CONFIRMATION) { + mHandler.postDelayed(() -> mCallback.onAction( + Callback.ACTION_AUTHENTICATED_AND_CONFIRMED), + getDelayAfterAuthenticatedDurationMs()); + } else { + mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED), + getDelayAfterAuthenticatedDurationMs()); + } break; case STATE_PENDING_CONFIRMATION: @@ -953,6 +961,10 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom return Utils.isDeviceCredentialAllowed(mPromptInfo); } + public LottieAnimationView getIconView() { + return mIconView; + } + @AuthDialog.DialogSize int getSize() { return mSize; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 49ac26411d3e..7f706859abb3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -213,6 +213,9 @@ public class AuthContainerView extends LinearLayout case AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR: mConfig.mCallback.onStartFingerprintNow(getRequestId()); break; + case AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED: + animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); + break; default: Log.e(TAG, "Unhandled action: " + action); } @@ -611,7 +614,11 @@ public class AuthContainerView extends LinearLayout return ((AuthBiometricFingerprintView) view).isUdfps(); } if (view instanceof BiometricPromptLayout) { - return ((BiometricPromptLayout) view).isUdfps(); + // this will force the prompt to align itself on the edge of the screen + // instead of centering (temporary workaround to prevent small implicit view + // from breaking due to the way gravity / margins are set in the legacy + // AuthPanelController + return true; } return false; @@ -635,12 +642,12 @@ public class AuthContainerView extends LinearLayout case Surface.ROTATION_90: mPanelController.setPosition(AuthPanelController.POSITION_RIGHT); - setScrollViewGravity(Gravity.BOTTOM | Gravity.RIGHT); + setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.RIGHT); break; case Surface.ROTATION_270: mPanelController.setPosition(AuthPanelController.POSITION_LEFT); - setScrollViewGravity(Gravity.BOTTOM | Gravity.LEFT); + setScrollViewGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); break; case Surface.ROTATION_180: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java index acdde3404ab5..167067e7d7e9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthPanelController.java @@ -114,7 +114,13 @@ public class AuthPanelController extends ViewOutlineProvider { } private int getTopBound(@Position int position) { - return Math.max(mContainerHeight - mContentHeight - mMargin, mMargin); + switch (position) { + case POSITION_LEFT: + case POSITION_RIGHT: + return Math.max((mContainerHeight - mContentHeight) / 2, mMargin); + default: + return Math.max(mContainerHeight - mContentHeight - mMargin, mMargin); + } } public void setContainerDimensions(int containerWidth, int containerHeight) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 630cfffe5969..d48b9c339d15 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -15,8 +15,6 @@ */ package com.android.systemui.biometrics -import android.animation.Animator -import android.animation.AnimatorListenerAdapter import android.app.ActivityTaskManager import android.content.Context import android.content.res.Configuration @@ -37,13 +35,13 @@ import android.os.Handler import android.util.Log import android.util.RotationUtils import android.view.Display +import android.view.DisplayInfo import android.view.Gravity import android.view.LayoutInflater import android.view.Surface import android.view.View import android.view.View.AccessibilityDelegate import android.view.ViewPropertyAnimator -import android.view.WindowInsets import android.view.WindowManager import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY @@ -61,7 +59,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.recents.OverviewProxyService +import com.android.systemui.util.boundsOnScreen import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.traceSection import java.io.PrintWriter @@ -83,7 +81,6 @@ constructor( fingerprintManager: FingerprintManager?, private val windowManager: WindowManager, private val activityTaskManager: ActivityTaskManager, - overviewProxyService: OverviewProxyService, displayManager: DisplayManager, private val displayStateInteractor: DisplayStateInteractor, @Main private val mainExecutor: DelayableExecutor, @@ -112,19 +109,6 @@ constructor( @VisibleForTesting val orientationListener = orientationReasonListener.orientationListener - @VisibleForTesting - val overviewProxyListener = - object : OverviewProxyService.OverviewProxyListener { - override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) { - overlayView?.let { view -> - handler.postDelayed({ updateOverlayVisibility(view) }, 500) - } - } - } - - private val animationDuration = - context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong() - private val isReverseDefaultRotation = context.resources.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation) @@ -142,12 +126,13 @@ constructor( field = value field?.let { newView -> windowManager.addView(newView, overlayViewParams) - updateOverlayVisibility(newView) orientationListener.enable() } } @VisibleForTesting var overlayOffsets: SensorLocationInternal = SensorLocationInternal.DEFAULT + private val displayInfo = DisplayInfo() + private val overlayViewParams = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, @@ -181,7 +166,6 @@ constructor( override fun hide(sensorId: Int) = hide(SideFpsUiRequestSource.AUTO_SHOW) } ) - overviewProxyService.addCallback(overviewProxyListener) listenForAlternateBouncerVisibility() dumpManager.registerDumpable(this) @@ -234,6 +218,23 @@ constructor( for (requestSource in requests) { pw.println(" $requestSource.name") } + + pw.println("overlayView:") + pw.println(" width=${overlayView?.width}") + pw.println(" height=${overlayView?.height}") + pw.println(" boundsOnScreen=${overlayView?.boundsOnScreen}") + + pw.println("displayStateInteractor:") + pw.println(" isInRearDisplayMode=${displayStateInteractor?.isInRearDisplayMode?.value}") + + pw.println("sensorProps:") + pw.println(" displayId=${displayInfo.uniqueId}") + pw.println(" sensorType=${sensorProps?.sensorType}") + pw.println(" location=${sensorProps?.getLocation(displayInfo.uniqueId)}") + + pw.println("overlayOffsets=$overlayOffsets") + pw.println("isReverseDefaultRotation=$isReverseDefaultRotation") + pw.println("currentRotation=${displayInfo.rotation}") } private fun onOrientationChanged(@BiometricOverlayConstants.ShowReason reason: Int) { @@ -246,6 +247,8 @@ constructor( val view = layoutInflater.inflate(R.layout.sidefps_view, null, false) overlayView = view val display = context.display!! + // b/284098873 `context.display.rotation` may not up-to-date, we use displayInfo.rotation + display.getDisplayInfo(displayInfo) val offsets = sensorProps.getLocation(display.uniqueId).let { location -> if (location == null) { @@ -259,12 +262,12 @@ constructor( view.rotation = display.asSideFpsAnimationRotation( offsets.isYAligned(), - getRotationFromDefault(display.rotation) + getRotationFromDefault(displayInfo.rotation) ) lottie.setAnimation( display.asSideFpsAnimation( offsets.isYAligned(), - getRotationFromDefault(display.rotation) + getRotationFromDefault(displayInfo.rotation) ) ) lottie.addLottieOnCompositionLoadedListener { @@ -340,45 +343,6 @@ constructor( windowManager.updateViewLayout(overlayView, overlayViewParams) } - private fun updateOverlayVisibility(view: View) { - if (view != overlayView) { - return - } - // hide after a few seconds if the sensor is oriented down and there are - // large overlapping system bars - var rotation = context.display?.rotation - - if (rotation != null) { - rotation = getRotationFromDefault(rotation) - } - - if ( - windowManager.currentWindowMetrics.windowInsets.hasBigNavigationBar() && - ((rotation == Surface.ROTATION_270 && overlayOffsets.isYAligned()) || - (rotation == Surface.ROTATION_180 && !overlayOffsets.isYAligned())) - ) { - overlayHideAnimator = - view - .animate() - .alpha(0f) - .setStartDelay(3_000) - .setDuration(animationDuration) - .setListener( - object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - view.visibility = View.GONE - overlayHideAnimator = null - } - } - ) - } else { - overlayHideAnimator?.cancel() - overlayHideAnimator = null - view.alpha = 1f - view.visibility = View.VISIBLE - } - } - private fun getRotationFromDefault(rotation: Int): Int = if (isReverseDefaultRotation) (rotation + 1) % 4 else rotation } @@ -426,9 +390,6 @@ private fun SensorLocationInternal.isYAligned(): Boolean = sensorLocationY != 0 private fun Display.isNaturalOrientation(): Boolean = rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 -private fun WindowInsets.hasBigNavigationBar(): Boolean = - getInsets(WindowInsets.Type.navigationBars()).bottom >= 70 - private fun LottieAnimationView.addOverlayDynamicColor( context: Context, @BiometricOverlayConstants.ShowReason reason: Int diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java index a2840fc1dc8c..056d692a3641 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java @@ -103,6 +103,13 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { } @Override + void onSensorRectUpdated(RectF bounds) { + super.onSensorRectUpdated(bounds); + bounds.round(this.mSensorBounds); + postInvalidate(); + } + + @Override void onDisplayConfiguring() { } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt index c935aa290e21..26b6f2a7a3cc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt @@ -78,6 +78,7 @@ constructor( sendFoldStateUpdate(isFolded) } } + sendFoldStateUpdate(false) screenSizeFoldProvider.registerCallback(callback, mainExecutor) awaitClose { screenSizeFoldProvider.unregisterCallback(callback) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt new file mode 100644 index 000000000000..bd0907e588ca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintIconViewBinder.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.biometrics.ui.binder + +import android.view.DisplayInfo +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.airbnb.lottie.LottieAnimationView +import com.android.systemui.biometrics.AuthBiometricFingerprintView +import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import kotlinx.coroutines.launch + +/** Sub-binder for [AuthBiometricFingerprintView.mIconView]. */ +object AuthBiometricFingerprintIconViewBinder { + + /** + * Binds a [AuthBiometricFingerprintView.mIconView] to a [AuthBiometricFingerprintViewModel]. + */ + @JvmStatic + fun bind(view: LottieAnimationView, viewModel: AuthBiometricFingerprintViewModel) { + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + val displayInfo = DisplayInfo() + view.context.display?.getDisplayInfo(displayInfo) + viewModel.setRotation(displayInfo.rotation) + viewModel.onConfigurationChanged(view.context.resources.configuration) + launch { viewModel.iconAsset.collect { iconAsset -> view.setAnimation(iconAsset) } } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt index ae0cf3771ed3..9c1bcec2f396 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt @@ -17,31 +17,18 @@ package com.android.systemui.biometrics.ui.binder -import android.view.Surface -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.biometrics.AuthBiometricFingerprintView import com.android.systemui.biometrics.ui.viewmodel.AuthBiometricFingerprintViewModel -import com.android.systemui.lifecycle.repeatWhenAttached -import kotlinx.coroutines.launch object AuthBiometricFingerprintViewBinder { - /** Binds a [AuthBiometricFingerprintView] to a [AuthBiometricFingerprintViewModel]. */ + /** + * Binds a [AuthBiometricFingerprintView.mIconView] to a [AuthBiometricFingerprintViewModel]. + */ @JvmStatic fun bind(view: AuthBiometricFingerprintView, viewModel: AuthBiometricFingerprintViewModel) { - view.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.onConfigurationChanged(view.context.resources.configuration) - viewModel.setRotation(view.context.display?.orientation ?: Surface.ROTATION_0) - launch { - viewModel.iconAsset.collect { iconAsset -> - if (view.isSfps) { - view.updateIconViewAnimation(iconAsset) - } - } - } - } + if (view.isSfps) { + AuthBiometricFingerprintIconViewBinder.bind(view.getIconView(), viewModel) } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index e4c4e9aedb56..1dffa80a084f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -19,8 +19,11 @@ package com.android.systemui.biometrics.ui.binder import android.animation.Animator import android.animation.AnimatorSet import android.animation.ValueAnimator +import android.view.Surface import android.view.View import android.view.ViewGroup +import android.view.WindowInsets +import android.view.WindowManager import android.view.accessibility.AccessibilityManager import android.widget.TextView import androidx.core.animation.addListener @@ -52,7 +55,9 @@ object BiometricViewSizeBinder { panelViewController: AuthPanelController, jankListener: BiometricJankListener, ) { - val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!! + val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java)) + val accessibilityManager = + requireNotNull(view.context.getSystemService(AccessibilityManager::class.java)) fun notifyAccessibilityChanged() { Utils.notifyAccessibilityContentChanged(accessibilityManager, view) } @@ -102,15 +107,26 @@ object BiometricViewSizeBinder { when { size.isSmall -> { iconHolderView.alpha = 1f + val bottomInset = + windowManager.maximumWindowMetrics.windowInsets + .getInsets(WindowInsets.Type.navigationBars()) + .bottom iconHolderView.y = - view.height - iconHolderView.height - iconPadding + if (view.isLandscape()) { + (view.height - iconHolderView.height - bottomInset) / 2f + } else { + view.height - + iconHolderView.height - + iconPadding - + bottomInset + } val newHeight = - iconHolderView.height + 2 * iconPadding.toInt() - + iconHolderView.height + (2 * iconPadding.toInt()) - iconHolderView.paddingTop - iconHolderView.paddingBottom panelViewController.updateForContentDimensions( width, - newHeight, + newHeight + bottomInset, 0, /* animateDurationMs */ ) } @@ -181,6 +197,11 @@ object BiometricViewSizeBinder { } } +private fun View.isLandscape(): Boolean { + val r = context.display.rotation + return r == Surface.ROTATION_90 || r == Surface.ROTATION_270 +} + private fun TextView.showTextOrHide(forceHide: Boolean = false) { visibility = if (forceHide || text.isBlank()) View.GONE else View.VISIBLE } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt index d7d17006168e..c92180647ab3 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt @@ -17,6 +17,7 @@ package com.android.systemui.controls.controller interface ControlsTileResourceConfiguration { + fun getPackageName(): String? fun getTileTitleId(): Int fun getTileImageId(): Int -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt index c96d3d4a2602..02490605fbb8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt @@ -20,12 +20,14 @@ import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject -/** - * Default Instance for ControlsTileResourceConfiguration. - */ +/** Default Instance for ControlsTileResourceConfiguration. */ @SysUISingleton -class ControlsTileResourceConfigurationImpl @Inject constructor() - : ControlsTileResourceConfiguration { +class ControlsTileResourceConfigurationImpl @Inject constructor() : + ControlsTileResourceConfiguration { + override fun getPackageName(): String? { + return null + } + override fun getTileTitleId(): Int { return R.string.quick_controls_title } @@ -33,4 +35,4 @@ class ControlsTileResourceConfigurationImpl @Inject constructor() override fun getTileImageId(): Int { return R.drawable.controls_icon } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt index 7509a8ad0c88..94e563319524 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt @@ -29,9 +29,9 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy -import kotlinx.coroutines.flow.StateFlow import java.util.Optional import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow /** * Pseudo-component to inject into classes outside `com.android.systemui.controls`. @@ -40,26 +40,26 @@ import javax.inject.Inject * instantiated if `featureEnabled` is true. Can also be queried for the availability of controls. */ @SysUISingleton -class ControlsComponent @Inject constructor( - @ControlsFeatureEnabled private val featureEnabled: Boolean, - private val context: Context, - private val lazyControlsController: Lazy<ControlsController>, - private val lazyControlsUiController: Lazy<ControlsUiController>, - private val lazyControlsListingController: Lazy<ControlsListingController>, - private val lockPatternUtils: LockPatternUtils, - private val keyguardStateController: KeyguardStateController, - private val userTracker: UserTracker, - controlsSettingsRepository: ControlsSettingsRepository, - optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration> +class ControlsComponent +@Inject +constructor( + @ControlsFeatureEnabled private val featureEnabled: Boolean, + private val context: Context, + private val lazyControlsController: Lazy<ControlsController>, + private val lazyControlsUiController: Lazy<ControlsUiController>, + private val lazyControlsListingController: Lazy<ControlsListingController>, + private val lockPatternUtils: LockPatternUtils, + private val keyguardStateController: KeyguardStateController, + private val userTracker: UserTracker, + controlsSettingsRepository: ControlsSettingsRepository, + optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration> ) { val canShowWhileLockedSetting: StateFlow<Boolean> = - controlsSettingsRepository.canShowControlsInLockscreen + controlsSettingsRepository.canShowControlsInLockscreen private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration = - optionalControlsTileResourceConfiguration.orElse( - ControlsTileResourceConfigurationImpl() - ) + optionalControlsTileResourceConfiguration.orElse(ControlsTileResourceConfigurationImpl()) fun getControlsController(): Optional<ControlsController> { return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty() @@ -77,9 +77,7 @@ class ControlsComponent @Inject constructor( } } - /** - * @return true if controls are feature-enabled and the user has the setting enabled - */ + /** @return true if controls are feature-enabled and the user has the setting enabled */ fun isEnabled() = featureEnabled /** @@ -90,8 +88,10 @@ class ControlsComponent @Inject constructor( */ fun getVisibility(): Visibility { if (!isEnabled()) return Visibility.UNAVAILABLE - if (lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) - == STRONG_AUTH_REQUIRED_AFTER_BOOT) { + if ( + lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) == + STRONG_AUTH_REQUIRED_AFTER_BOOT + ) { return Visibility.AVAILABLE_AFTER_UNLOCK } if (!canShowWhileLockedSetting.value && !keyguardStateController.isUnlocked()) { @@ -102,7 +102,13 @@ class ControlsComponent @Inject constructor( } enum class Visibility { - AVAILABLE, AVAILABLE_AFTER_UNLOCK, UNAVAILABLE + AVAILABLE, + AVAILABLE_AFTER_UNLOCK, + UNAVAILABLE + } + + fun getPackageName(): String? { + return controlsTileResourceConfiguration.getPackageName() } fun getTileTitleId(): Int { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt index 78e132ff6397..4b297a3d321d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt @@ -33,10 +33,10 @@ import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.plugins.BcSmartspaceDataPlugin.UI_SURFACE_DREAM import com.android.systemui.smartspace.SmartspacePrecondition import com.android.systemui.smartspace.SmartspaceTargetFilter -import com.android.systemui.smartspace.dagger.SmartspaceModule import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_PRECONDITION import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_SMARTSPACE_TARGET_FILTER +import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN import com.android.systemui.smartspace.dagger.SmartspaceViewComponent import com.android.systemui.util.concurrency.Execution import java.util.Optional @@ -58,7 +58,7 @@ class DreamSmartspaceController @Inject constructor( @Named(DREAM_SMARTSPACE_TARGET_FILTER) private val optionalTargetFilter: Optional<SmartspaceTargetFilter>, @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin>, - @Named(SmartspaceModule.WEATHER_SMARTSPACE_DATA_PLUGIN) + @Named(DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN) optionalWeatherPlugin: Optional<BcSmartspaceDataPlugin>, ) { companion object { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index d6c082928168..f7aa0acfd936 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -93,7 +93,7 @@ object Flags { // TODO(b/277338665): Tracking Bug @JvmField val NOTIFICATION_SHELF_REFACTOR = - unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true) + unreleasedFlag(271161129, "notification_shelf_refactor") @JvmField val ANIMATED_NOTIFICATION_SHADE_INSETS = @@ -138,7 +138,7 @@ object Flags { // TODO(b/275694445): Tracking Bug @JvmField - val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = unreleasedFlag(208, + val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = releasedFlag(208, "lockscreen_without_secure_lock_when_dreaming") /** @@ -601,7 +601,7 @@ object Flags { // 1300 - screenshots // TODO(b/264916608): Tracking Bug - @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata", teamfood = true) + @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata") // TODO(b/266955521): Tracking bug @JvmField val SCREENSHOT_DETECTION = releasedFlag(1303, "screenshot_detection") @@ -678,7 +678,7 @@ object Flags { // TODO(b/283071711): Tracking bug @JvmField val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK = - unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock") + releasedFlag(2401, "trim_resources_with_background_trim_on_lock") // TODO:(b/283203305): Tracking bug @JvmField @@ -728,6 +728,13 @@ object Flags { @JvmField val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter") + // 2900 - Zero Jank fixes. Naming convention is: zj_<bug number>_<cuj name> + + // TODO:(b/285623104): Tracking bug + @JvmField + val ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD = + releasedFlag(2900, "zj_285570694_lockscreen_transition_from_aod") + // TODO(b/283084712): Tracking Bug @JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt index 27a5974e6299..bc0713952ce1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard import android.content.ContentProvider import android.content.ContentValues import android.content.Context +import android.content.Intent import android.content.UriMatcher import android.content.pm.PackageManager import android.content.pm.ProviderInfo @@ -282,11 +283,11 @@ class CustomizationProvider : Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON, Contract.LockScreenQuickAffordances.AffordanceTable.Columns.IS_ENABLED, Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_INSTRUCTIONS, + .ENABLEMENT_EXPLANATION, Contract.LockScreenQuickAffordances.AffordanceTable.Columns .ENABLEMENT_ACTION_TEXT, Contract.LockScreenQuickAffordances.AffordanceTable.Columns - .ENABLEMENT_COMPONENT_NAME, + .ENABLEMENT_ACTION_INTENT, Contract.LockScreenQuickAffordances.AffordanceTable.Columns.CONFIGURE_INTENT, ) ) @@ -298,13 +299,10 @@ class CustomizationProvider : representation.name, representation.iconResourceId, if (representation.isEnabled) 1 else 0, - representation.instructions?.joinToString( - Contract.LockScreenQuickAffordances.AffordanceTable - .ENABLEMENT_INSTRUCTIONS_DELIMITER - ), + representation.explanation, representation.actionText, - representation.actionComponentName, - representation.configureIntent?.toUri(0), + representation.actionIntent?.toUri(Intent.URI_INTENT_SCHEME), + representation.configureIntent?.toUri(Intent.URI_INTENT_SCHEME), ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index a8d22c48e709..94227bccfced 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -19,6 +19,7 @@ package com.android.systemui.keyguard; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.view.RemoteAnimationTarget.MODE_CLOSING; import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; @@ -29,6 +30,7 @@ import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_NONE; +import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TransitionFlags; import static android.view.WindowManager.TransitionOldType; import static android.view.WindowManager.TransitionType; @@ -49,6 +51,7 @@ import android.os.RemoteException; import android.os.Trace; import android.util.ArrayMap; import android.util.Log; +import android.util.RotationUtils; import android.util.Slog; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; @@ -73,6 +76,7 @@ import com.android.systemui.SystemUIApplication; import com.android.systemui.settings.DisplayTracker; import com.android.wm.shell.transition.ShellTransitions; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.util.CounterRotator; import com.android.wm.shell.util.TransitionUtil; import java.util.ArrayList; @@ -103,7 +107,8 @@ public class KeyguardService extends Service { } private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers, - SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { + SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap, + CounterRotator counterWallpaper) { final ArrayList<RemoteAnimationTarget> out = new ArrayList<>(); for (int i = 0; i < info.getChanges().size(); i++) { boolean changeIsWallpaper = @@ -133,6 +138,25 @@ public class KeyguardService extends Service { (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0, info, t, leashMap); + if (changeIsWallpaper) { + int rotateDelta = RotationUtils.deltaRotation(change.getStartRotation(), + change.getEndRotation()); + if (rotateDelta != 0 && change.getParent() != null + && change.getMode() == TRANSIT_TO_BACK) { + final TransitionInfo.Change parent = info.getChange(change.getParent()); + if (parent != null) { + float displayW = parent.getEndAbsBounds().width(); + float displayH = parent.getEndAbsBounds().height(); + counterWallpaper.setup(t, parent.getLeash(), rotateDelta, displayW, + displayH); + } + if (counterWallpaper.getSurface() != null) { + t.setLayer(counterWallpaper.getSurface(), -1); + counterWallpaper.addChild(t, leashMap.get(change.getLeash())); + } + } + } + out.add(target); } return out.toArray(new RemoteAnimationTarget[out.size()]); @@ -159,10 +183,13 @@ public class KeyguardService extends Service { // Wrap Keyguard going away animation. // Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy). - public static IRemoteTransition wrap(IRemoteAnimationRunner runner) { + public static IRemoteTransition wrap(final KeyguardViewMediator keyguardViewMediator, + final IRemoteAnimationRunner runner, final boolean lockscreenLiveWallpaperEnabled) { return new IRemoteTransition.Stub() { private final ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = new ArrayMap<>(); + private final CounterRotator mCounterRotator = new CounterRotator(); + @GuardedBy("mLeashMap") private IRemoteTransitionFinishedCallback mFinishCallback = null; @@ -175,9 +202,9 @@ public class KeyguardService extends Service { synchronized (mLeashMap) { final RemoteAnimationTarget[] apps = - wrap(info, false /* wallpapers */, t, mLeashMap); + wrap(info, false /* wallpapers */, t, mLeashMap, mCounterRotator); final RemoteAnimationTarget[] wallpapers = - wrap(info, true /* wallpapers */, t, mLeashMap); + wrap(info, true /* wallpapers */, t, mLeashMap, mCounterRotator); final RemoteAnimationTarget[] nonApps = new RemoteAnimationTarget[0]; // Set alpha back to 1 for the independent changes because we will be animating @@ -188,7 +215,9 @@ public class KeyguardService extends Service { } } initAlphaForAnimationTargets(t, apps); - initAlphaForAnimationTargets(t, wallpapers); + if (lockscreenLiveWallpaperEnabled) { + initAlphaForAnimationTargets(t, wallpapers); + } t.apply(); mFinishCallback = finishCallback; runner.onAnimationStart( @@ -211,6 +240,12 @@ public class KeyguardService extends Service { SurfaceControl.Transaction candidateT, IBinder currentTransition, IRemoteTransitionFinishedCallback candidateFinishCallback) throws RemoteException { + if ((candidateInfo.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) { + keyguardViewMediator.setPendingLock(true); + keyguardViewMediator.cancelKeyguardExitAnimation(); + return; + } + try { synchronized (mLeashMap) { runner.onAnimationCancelled(); @@ -231,11 +266,19 @@ public class KeyguardService extends Service { @GuardedBy("mLeashMap") private void finish() throws RemoteException { + SurfaceControl.Transaction finishTransaction = null; + if (mCounterRotator.getSurface() != null + && mCounterRotator.getSurface().isValid()) { + finishTransaction = new SurfaceControl.Transaction(); + mCounterRotator.cleanUp(finishTransaction); + } mLeashMap.clear(); final IRemoteTransitionFinishedCallback finishCallback = mFinishCallback; if (finishCallback != null) { mFinishCallback = null; - finishCallback.onTransitionFinished(null /* wct */, null /* t */); + finishCallback.onTransitionFinished(null /* wct */, finishTransaction); + } else if (finishTransaction != null) { + finishTransaction.apply(); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 29a7fe7d061a..68e72c58972b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator +import android.app.WallpaperManager import android.content.Context import android.graphics.Matrix import android.graphics.Rect @@ -33,10 +34,10 @@ import android.view.SyncRtSurfaceTransactionApplier import android.view.View import androidx.annotation.VisibleForTesting import androidx.core.math.MathUtils +import com.android.app.animation.Interpolators import com.android.internal.R import com.android.keyguard.KeyguardClockSwitchController import com.android.keyguard.KeyguardViewController -import com.android.app.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -50,6 +51,7 @@ import com.android.systemui.shared.system.smartspace.SmartspaceState import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController +import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy import javax.inject.Inject @@ -148,7 +150,8 @@ class KeyguardUnlockAnimationController @Inject constructor( private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>, private val statusBarStateController: SysuiStatusBarStateController, private val notificationShadeWindowController: NotificationShadeWindowController, - private val powerManager: PowerManager + private val powerManager: PowerManager, + private val wallpaperManager: WallpaperManager ) : KeyguardStateController.Callback, ISysuiUnlockAnimationController.Stub() { interface KeyguardUnlockAnimationListener { @@ -171,7 +174,7 @@ class KeyguardUnlockAnimationController @Inject constructor( @JvmDefault fun onUnlockAnimationStarted( playingCannedAnimation: Boolean, - fromWakeAndUnlock: Boolean, + isWakeAndUnlockNotFromDream: Boolean, unlockAnimationStartDelay: Long, unlockAnimationDuration: Long ) {} @@ -204,6 +207,12 @@ class KeyguardUnlockAnimationController @Inject constructor( var playingCannedUnlockAnimation = false /** + * Whether we reached the swipe gesture threshold to dismiss keyguard, or restore it, once + * and should ignore any future changes to the dismiss amount before the animation finishes. + */ + var dismissAmountThresholdsReached = false + + /** * Remote callback provided by Launcher that allows us to control the Launcher's unlock * animation and smartspace. * @@ -433,7 +442,14 @@ class KeyguardUnlockAnimationController @Inject constructor( // animate state. if (!keyguardStateController.isKeyguardGoingAway && willUnlockWithInWindowLauncherAnimations) { - launcherUnlockController?.setUnlockAmount(1f, true /* forceIfAnimating */) + try { + launcherUnlockController?.setUnlockAmount(1f, true /* forceIfAnimating */) + } catch (e: DeadObjectException) { + Log.e(TAG, "launcherUnlockAnimationController was dead, but non-null in " + + "onKeyguardGoingAwayChanged(). Catching exception as this should mean " + + "Launcher is in the process of being destroyed, but the IPC to System UI " + + "telling us hasn't arrived yet.") + } } } @@ -575,10 +591,13 @@ class KeyguardUnlockAnimationController @Inject constructor( playCannedUnlockAnimation() } + // Notify if waking from AOD only + val isWakeAndUnlockNotFromDream = biometricUnlockControllerLazy.get().isWakeAndUnlock && + biometricUnlockControllerLazy.get().mode != MODE_WAKE_AND_UNLOCK_FROM_DREAM listeners.forEach { it.onUnlockAnimationStarted( playingCannedUnlockAnimation /* playingCannedAnimation */, - biometricUnlockControllerLazy.get().isWakeAndUnlock /* isWakeAndUnlock */, + isWakeAndUnlockNotFromDream /* isWakeAndUnlockNotFromDream */, CANNED_UNLOCK_START_DELAY /* unlockStartDelay */, LAUNCHER_ICONS_ANIMATION_DURATION_MS /* unlockAnimationDuration */) } @@ -640,6 +659,7 @@ class KeyguardUnlockAnimationController @Inject constructor( @VisibleForTesting fun unlockToLauncherWithInWindowAnimations() { + surfaceBehindAlpha = 1f setSurfaceBehindAppearAmount(1f, wallpapers = false) try { @@ -679,8 +699,10 @@ class KeyguardUnlockAnimationController @Inject constructor( return@postDelayed } - if (wallpaperTargets != null) { - fadeInWallpaper() + if ((wallpaperTargets?.isNotEmpty() == true) && + wallpaperManager.isLockscreenLiveWallpaperEnabled()) { + fadeInWallpaper() + hideKeyguardViewAfterRemoteAnimation() } else { keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( false /* cancelled */) @@ -751,6 +773,10 @@ class KeyguardUnlockAnimationController @Inject constructor( return } + if (dismissAmountThresholdsReached) { + return + } + if (!keyguardStateController.isShowing) { return } @@ -782,6 +808,11 @@ class KeyguardUnlockAnimationController @Inject constructor( return } + // no-op if we alreaddy reached a threshold. + if (dismissAmountThresholdsReached) { + return + } + // no-op if animation is not requested yet. if (!keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() || !keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) { @@ -796,6 +827,7 @@ class KeyguardUnlockAnimationController @Inject constructor( !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture && dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)) { setSurfaceBehindAppearAmount(1f) + dismissAmountThresholdsReached = true keyguardViewMediator.get().exitKeyguardAndFinishSurfaceBehindRemoteAnimation( false /* cancelled */) } @@ -930,6 +962,7 @@ class KeyguardUnlockAnimationController @Inject constructor( wallpaperTargets = null playingCannedUnlockAnimation = false + dismissAmountThresholdsReached = false willUnlockWithInWindowLauncherAnimations = false willUnlockWithSmartspaceTransition = false @@ -954,7 +987,7 @@ class KeyguardUnlockAnimationController @Inject constructor( 0 /* fadeOutDuration */ ) } else { - Log.e(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " + + Log.i(TAG, "#hideKeyguardViewAfterRemoteAnimation called when keyguard view is not " + "showing. Ignoring...") } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 3b40d8602e6f..0e0cf74ba463 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -45,6 +45,7 @@ import android.app.AlarmManager; import android.app.BroadcastOptions; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.app.WallpaperManager; import android.app.WindowConfiguration; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; @@ -93,7 +94,6 @@ import android.view.WindowManager; import android.view.WindowManagerPolicyConstants; import android.view.animation.Animation; import android.view.animation.AnimationUtils; -import android.window.IRemoteTransition; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -161,7 +161,6 @@ import dagger.Lazy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Optional; import java.util.concurrent.Executor; /** @@ -280,6 +279,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private AlarmManager mAlarmManager; private AudioManager mAudioManager; private StatusBarManager mStatusBarManager; + private WallpaperManager mWallpaperManager; private final IStatusBarService mStatusBarService; private final IBinder mStatusBarDisableToken = new Binder(); private final UserTracker mUserTracker; @@ -697,6 +697,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } break; + case TelephonyManager.SIM_STATE_UNKNOWN: + mPendingPinLock = false; + break; default: if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState); break; @@ -1350,11 +1353,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, setShowingLocked(false /* showing */, true /* forceCallbacks */); } + boolean isLLwpEnabled = getWallpaperManager().isLockscreenLiveWallpaperEnabled(); mKeyguardTransitions.register( - KeyguardService.wrap(getExitAnimationRunner()), - KeyguardService.wrap(getOccludeAnimationRunner()), - KeyguardService.wrap(getOccludeByDreamAnimationRunner()), - KeyguardService.wrap(getUnoccludeAnimationRunner())); + KeyguardService.wrap(this, getExitAnimationRunner(), isLLwpEnabled), + KeyguardService.wrap(this, getOccludeAnimationRunner(), isLLwpEnabled), + KeyguardService.wrap(this, getOccludeByDreamAnimationRunner(), isLLwpEnabled), + KeyguardService.wrap(this, getUnoccludeAnimationRunner(), isLLwpEnabled)); final ContentResolver cr = mContext.getContentResolver(); @@ -1400,6 +1404,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mWorkLockController = new WorkLockActivityController(mContext, mUserTracker); } + // TODO(b/273443374) remove, temporary util to get a feature flag + private WallpaperManager getWallpaperManager() { + if (mWallpaperManager == null) { + mWallpaperManager = mContext.getSystemService(WallpaperManager.class); + } + return mWallpaperManager; + } + @Override public void start() { synchronized (this) { @@ -1902,19 +1914,19 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public IRemoteAnimationRunner getExitAnimationRunner() { - return mExitAnimationRunner; + return validatingRemoteAnimationRunner(mExitAnimationRunner); } public IRemoteAnimationRunner getOccludeAnimationRunner() { - return mOccludeAnimationRunner; + return validatingRemoteAnimationRunner(mOccludeAnimationRunner); } public IRemoteAnimationRunner getOccludeByDreamAnimationRunner() { - return mOccludeByDreamAnimationRunner; + return validatingRemoteAnimationRunner(mOccludeByDreamAnimationRunner); } public IRemoteAnimationRunner getUnoccludeAnimationRunner() { - return mUnoccludeAnimationRunner; + return validatingRemoteAnimationRunner(mUnoccludeAnimationRunner); } public boolean isHiding() { @@ -1943,7 +1955,8 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, startKeyguardExitAnimation(0, 0); } - mPowerGestureIntercepted = mUpdateMonitor.isSecureCameraLaunchedOverKeyguard(); + mPowerGestureIntercepted = + isOccluded && mUpdateMonitor.isSecureCameraLaunchedOverKeyguard(); if (mOccluded != isOccluded) { mOccluded = isOccluded; @@ -2894,6 +2907,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // re-locking. We should just end the surface-behind animation without exiting the // keyguard. The pending lock will be handled by onFinishedGoingToSleep(). finishSurfaceBehindRemoteAnimation(true); + maybeHandlePendingLock(); } else { Log.d(TAG, "#handleCancelKeyguardExitAnimation: keyguard exit animation cancelled. " + "No pending lock, we should end up unlocked with the app/launcher visible."); @@ -3249,8 +3263,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, /** * Cancel the keyguard exit animation, usually because we were swiping to unlock but WM starts * a new remote animation before finishing the keyguard exit animation. - * - * This will dismiss the keyguard. */ public void cancelKeyguardExitAnimation() { Trace.beginSection("KeyguardViewMediator#cancelKeyguardExitAnimation"); @@ -3423,11 +3435,15 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } } - private void setPendingLock(boolean hasPendingLock) { + public void setPendingLock(boolean hasPendingLock) { mPendingLock = hasPendingLock; Trace.traceCounter(Trace.TRACE_TAG_APP, "pendingLock", mPendingLock ? 1 : 0); } + private boolean isViewRootReady() { + return mKeyguardViewControllerLazy.get().getViewRootImpl() != null; + } + public void addStateMonitorCallback(IKeyguardStateCallback callback) { synchronized (this) { mKeyguardStateCallbacks.add(callback); @@ -3530,4 +3546,27 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION); } } + + private IRemoteAnimationRunner validatingRemoteAnimationRunner(IRemoteAnimationRunner wrapped) { + return new IRemoteAnimationRunner.Stub() { + @Override + public void onAnimationCancelled() throws RemoteException { + wrapped.onAnimationCancelled(); + } + + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) + throws RemoteException { + if (!isViewRootReady()) { + Log.w(TAG, "Skipping remote animation - view root not ready"); + return; + } + + wrapped.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback); + } + }; + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt index c0a5a51a910d..4dc52ff46707 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt @@ -41,6 +41,8 @@ class BouncerMessageView : LinearLayout { super.onFinishInflate() primaryMessageView = findViewById(R.id.bouncer_primary_message_area) secondaryMessageView = findViewById(R.id.bouncer_secondary_message_area) + primaryMessageView?.disable() + secondaryMessageView?.disable() } fun init(factory: KeyguardMessageAreaController.Factory) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt index c9f645dddd8d..af0abdff0c62 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/CameraQuickAffordanceConfig.kt @@ -52,8 +52,7 @@ constructor( override val key: String get() = BuiltInKeyguardQuickAffordanceKeys.CAMERA - override val pickerName: String - get() = context.getString(R.string.accessibility_camera_button) + override fun pickerName(): String = context.getString(R.string.accessibility_camera_button) override val pickerIconResourceId: Int get() = R.drawable.ic_camera diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt index ef0c9a175141..16385ec59aa7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt @@ -99,7 +99,7 @@ constructor( override val key: String = BuiltInKeyguardQuickAffordanceKeys.DO_NOT_DISTURB - override val pickerName: String = context.getString(R.string.quick_settings_dnd_label) + override fun pickerName(): String = context.getString(R.string.quick_settings_dnd_label) override val pickerIconResourceId: Int = R.drawable.ic_do_not_disturb diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt index 3412f35669e3..ed8823a53fe2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt @@ -75,8 +75,7 @@ constructor( override val key: String get() = BuiltInKeyguardQuickAffordanceKeys.FLASHLIGHT - override val pickerName: String - get() = context.getString(R.string.quick_settings_flashlight_label) + override fun pickerName(): String = context.getString(R.string.quick_settings_flashlight_label) override val pickerIconResourceId: Int get() = R.drawable.ic_flashlight_off diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index a1e9137d1764..f3fc8096f5d7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.ComponentName import android.content.Context import android.content.Intent import androidx.annotation.DrawableRes @@ -34,6 +35,7 @@ import com.android.systemui.controls.ui.ControlsActivity import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.appStoreIntent import com.android.systemui.util.kotlin.getOrNull import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose @@ -54,7 +56,7 @@ constructor( override val key: String = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS - override val pickerName: String by lazy { context.getString(component.getTileTitleId()) } + override fun pickerName(): String = context.getString(component.getTileTitleId()) override val pickerIconResourceId: Int by lazy { component.getTileImageId() } @@ -76,18 +78,44 @@ constructor( component.getControlsListingController().getOrNull()?.getCurrentServices() val hasFavorites = component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true - if (currentServices.isNullOrEmpty() || !hasFavorites) { - return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( + val componentPackageName = component.getPackageName() + when { + currentServices.isNullOrEmpty() && !componentPackageName.isNullOrEmpty() -> { + // No home app installed but we know which app we want to install. + return disabledPickerState( + explanation = context.getString( - R.string.keyguard_affordance_enablement_dialog_home_instruction_1 + R.string.home_quick_affordance_unavailable_install_the_app ), + actionText = context.getString(R.string.install_app), + actionIntent = appStoreIntent(context, componentPackageName), + ) + } + currentServices.isNullOrEmpty() && componentPackageName.isNullOrEmpty() -> { + // No home app installed and we don't know which app we want to install. + return disabledPickerState( + explanation = context.getString( - R.string.keyguard_affordance_enablement_dialog_home_instruction_2 + R.string.home_quick_affordance_unavailable_install_the_app ), - ), - ) + ) + } + !hasFavorites -> { + // Home app installed but no favorites selected. + val activityClass = component.getControlsUiController().get().resolveActivity() + return disabledPickerState( + explanation = + context.getString( + R.string.home_quick_affordance_unavailable_configure_the_app + ), + actionText = context.getString(R.string.controls_open_app), + actionIntent = + Intent().apply { + component = ComponentName(context, activityClass) + putExtra(ControlsUiController.EXTRA_ANIMATE, true) + }, + ) + } } return KeyguardQuickAffordanceConfig.PickerScreenState.Default() @@ -172,6 +200,20 @@ constructor( } } + private fun disabledPickerState( + explanation: String, + actionText: String? = null, + actionIntent: Intent? = null, + ): KeyguardQuickAffordanceConfig.PickerScreenState.Disabled { + check(actionIntent == null || actionText != null) + + return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + explanation = explanation, + actionText = actionText, + actionIntent = actionIntent, + ) + } + companion object { private const val TAG = "HomeControlsKeyguardQuickAffordanceConfig" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt index e32edcb010e8..320d158fb13e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt @@ -18,11 +18,13 @@ package com.android.systemui.keyguard.data.quickaffordance import android.app.AlertDialog +import android.content.Context import android.content.Intent +import android.net.Uri +import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.keyguard.shared.quickaffordance.ActivationState -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract import kotlinx.coroutines.flow.Flow /** Defines interface that can act as data source for a single quick affordance model. */ @@ -31,8 +33,6 @@ interface KeyguardQuickAffordanceConfig { /** Unique identifier for this quick affordance. It must be globally unique. */ val key: String - val pickerName: String - val pickerIconResourceId: Int /** @@ -43,6 +43,12 @@ interface KeyguardQuickAffordanceConfig { val lockScreenState: Flow<LockScreenState> /** + * Returns a user-visible [String] that should be shown as the name for the option in the + * wallpaper picker / settings app to select this quick affordance. + */ + fun pickerName(): String + + /** * Returns the [PickerScreenState] representing the affordance in the settings or selector * experience. */ @@ -76,37 +82,35 @@ interface KeyguardQuickAffordanceConfig { /** * The picker shows the item for selecting this affordance as disabled. Clicking on it will - * show the given instructions to the user. If [actionText] and [actionComponentName] are - * provided (optional) a button will be shown to open an activity to help the user complete - * the steps described in the instructions. + * show the given instructions to the user. If [actionText] and [actionIntent] are provided + * (optional) a button will be shown to open an activity to help the user complete the steps + * described in the instructions. */ data class Disabled( - /** List of human-readable instructions for setting up the quick affordance. */ - val instructions: List<String>, + /** Human-readable explanation as to why the quick affordance is current disabled. */ + val explanation: String, /** * Optional text to display on a button that the user can click to start a flow to go * and set up the quick affordance and make it enabled. */ val actionText: String? = null, /** - * Optional component name to be able to build an `Intent` that opens an `Activity` for - * the user to be able to set up the quick affordance and make it enabled. - * - * This is either just an action for the `Intent` or a package name and action, - * separated by - * [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for - * convenience, you can use the [componentName] function. + * Optional [Intent] that opens an `Activity` for the user to be able to set up the + * quick affordance and make it enabled. */ - val actionComponentName: String? = null, + val actionIntent: Intent? = null, ) : PickerScreenState() { init { - check(instructions.isNotEmpty()) { "Instructions must not be empty!" } + check(explanation.isNotEmpty()) { "Explanation must not be empty!" } check( - (actionText.isNullOrEmpty() && actionComponentName.isNullOrEmpty()) || - (!actionText.isNullOrEmpty() && !actionComponentName.isNullOrEmpty()) + (actionText.isNullOrEmpty() && actionIntent == null) || + (!actionText.isNullOrEmpty() && actionIntent != null) ) { - "actionText and actionComponentName must either both be null/empty or both be" + - " non-empty!" + """ + actionText and actionIntent must either both be null/empty or both be + non-null and non-empty! + """ + .trimIndent() } } } @@ -159,17 +163,33 @@ interface KeyguardQuickAffordanceConfig { } companion object { - fun componentName( - packageName: String? = null, - action: String?, - ): String? { - return when { - action.isNullOrEmpty() -> null - !packageName.isNullOrEmpty() -> - "$packageName${Contract.LockScreenQuickAffordances.AffordanceTable - .COMPONENT_NAME_SEPARATOR}$action" - else -> action + + /** + * Returns an [Intent] that can be used to start an activity that opens the app store app to + * a page showing the app with the passed-in [packageName]. + * + * If the feature isn't enabled on this device/variant/configuration, a `null` will be + * returned. + */ + fun appStoreIntent(context: Context, packageName: String?): Intent? { + if (packageName.isNullOrEmpty()) { + return null + } + + val appStorePackageName = context.getString(R.string.config_appStorePackageName) + val linkTemplate = context.getString(R.string.config_appStoreAppLinkTemplate) + if (appStorePackageName.isEmpty() || linkTemplate.isEmpty()) { + return null + } + + check(linkTemplate.contains(APP_PACKAGE_NAME_PLACEHOLDER)) + + return Intent(Intent.ACTION_VIEW).apply { + setPackage(appStorePackageName) + data = Uri.parse(linkTemplate.replace(APP_PACKAGE_NAME_PLACEHOLDER, packageName)) } } + + private const val APP_PACKAGE_NAME_PLACEHOLDER = "\$packageName" } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt index da91572894f3..250356819baf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt @@ -34,6 +34,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.util.RingerModeTracker +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose @@ -45,30 +46,32 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import javax.inject.Inject @SysUISingleton -class MuteQuickAffordanceConfig @Inject constructor( - context: Context, - private val userTracker: UserTracker, - private val userFileManager: UserFileManager, - private val ringerModeTracker: RingerModeTracker, - private val audioManager: AudioManager, - @Application private val coroutineScope: CoroutineScope, - @Main private val mainDispatcher: CoroutineDispatcher, - @Background private val backgroundDispatcher: CoroutineDispatcher, +class MuteQuickAffordanceConfig +@Inject +constructor( + private val context: Context, + private val userTracker: UserTracker, + private val userFileManager: UserFileManager, + private val ringerModeTracker: RingerModeTracker, + private val audioManager: AudioManager, + @Application private val coroutineScope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, + @Background private val backgroundDispatcher: CoroutineDispatcher, ) : KeyguardQuickAffordanceConfig { private var previousNonSilentMode: Int = DEFAULT_LAST_NON_SILENT_VALUE override val key: String = BuiltInKeyguardQuickAffordanceKeys.MUTE - override val pickerName: String = context.getString(R.string.volume_ringer_status_silent) + override fun pickerName(): String = context.getString(R.string.volume_ringer_status_silent) override val pickerIconResourceId: Int = R.drawable.ic_notifications_silence override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = - ringerModeTracker.ringerModeInternal.asFlow() + ringerModeTracker.ringerModeInternal + .asFlow() .onStart { getLastNonSilentRingerMode() } .distinctUntilChanged() .onEach { mode -> @@ -78,17 +81,14 @@ class MuteQuickAffordanceConfig @Inject constructor( } } .map { mode -> - val (activationState, contentDescriptionRes) = when { - audioManager.isVolumeFixed -> - ActivationState.NotSupported to - R.string.volume_ringer_hint_mute - mode == AudioManager.RINGER_MODE_SILENT -> - ActivationState.Active to - R.string.volume_ringer_hint_mute - else -> - ActivationState.Inactive to - R.string.volume_ringer_hint_unmute - } + val (activationState, contentDescriptionRes) = + when { + audioManager.isVolumeFixed -> + ActivationState.NotSupported to R.string.volume_ringer_hint_mute + mode == AudioManager.RINGER_MODE_SILENT -> + ActivationState.Active to R.string.volume_ringer_hint_mute + else -> ActivationState.Inactive to R.string.volume_ringer_hint_unmute + } KeyguardQuickAffordanceConfig.LockScreenState.Visible( Icon.Resource( @@ -130,32 +130,35 @@ class MuteQuickAffordanceConfig @Inject constructor( } /** - * Gets the last non-silent ringer mode from shared-preferences if it exists. This is - * cached by [MuteQuickAffordanceCoreStartable] while this affordance is selected + * Gets the last non-silent ringer mode from shared-preferences if it exists. This is cached by + * [MuteQuickAffordanceCoreStartable] while this affordance is selected */ private suspend fun getLastNonSilentRingerMode(): Int = withContext(backgroundDispatcher) { - userFileManager.getSharedPreferences( + userFileManager + .getSharedPreferences( MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME, Context.MODE_PRIVATE, userTracker.userId - ).getInt( + ) + .getInt( LAST_NON_SILENT_RINGER_MODE_KEY, ringerModeTracker.ringerModeInternal.value ?: DEFAULT_LAST_NON_SILENT_VALUE - ) + ) } private fun <T> LiveData<T>.asFlow(): Flow<T?> = conflatedCallbackFlow { - val observer = Observer { value: T -> trySend(value) } - observeForever(observer) - send(value) - awaitClose { removeObserver(observer) } - }.flowOn(mainDispatcher) + val observer = Observer { value: T -> trySend(value) } + observeForever(observer) + send(value) + awaitClose { removeObserver(observer) } + } + .flowOn(mainDispatcher) companion object { const val LAST_NON_SILENT_RINGER_MODE_KEY = "key_last_non_silent_ringer_mode" const val MUTE_QUICK_AFFORDANCE_PREFS_FILE_NAME = "quick_affordance_mute_ringer_mode_cache" private const val DEFAULT_LAST_NON_SILENT_VALUE = AudioManager.RINGER_MODE_NORMAL } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt index ea6c107cd161..20ed549e42d4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt @@ -42,7 +42,7 @@ constructor( override val key: String = BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER - override val pickerName = context.getString(R.string.qr_code_scanner_title) + override fun pickerName(): String = context.getString(R.string.qr_code_scanner_title) override val pickerIconResourceId = R.drawable.ic_qr_code_scanner @@ -53,6 +53,7 @@ constructor( override fun onQRCodeScannerActivityChanged() { trySendWithFailureLogging(state(), TAG) } + override fun onQRCodeScannerPreferenceChanged() { trySendWithFailureLogging(state(), TAG) } @@ -79,16 +80,14 @@ constructor( return when { !controller.isAvailableOnDevice -> KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice - !controller.isAbleToOpenCameraApp -> + !controller.isAbleToOpenCameraApp -> { KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( - context.getString( - R.string - .keyguard_affordance_enablement_dialog_qr_scanner_instruction - ), + explanation = + context.getString( + R.string.qr_scanner_quick_affordance_unavailable_explanation ), ) + } else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default() } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt index 12270784adca..c019d21e00ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt @@ -51,7 +51,7 @@ constructor( override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET - override val pickerName: String = context.getString(R.string.accessibility_wallet_button) + override fun pickerName(): String = context.getString(R.string.accessibility_wallet_button) override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen @@ -63,7 +63,7 @@ constructor( val hasCards = response?.walletCards?.isNotEmpty() == true trySendWithFailureLogging( state( - isFeatureEnabled = walletController.isWalletEnabled, + isFeatureEnabled = isWalletAvailable(), hasCard = hasCards, tileIcon = walletController.walletClient.tileIcon, ), @@ -100,19 +100,20 @@ constructor( return when { !walletController.walletClient.isWalletServiceAvailable -> KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice - !walletController.isWalletEnabled || queryCards().isEmpty() -> { + !isWalletAvailable() -> KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - instructions = - listOf( - context.getString( - R.string.keyguard_affordance_enablement_dialog_wallet_instruction_1 - ), - context.getString( - R.string.keyguard_affordance_enablement_dialog_wallet_instruction_2 - ), + explanation = + context.getString( + R.string.wallet_quick_affordance_unavailable_install_the_app + ), + ) + queryCards().isEmpty() -> + KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + explanation = + context.getString( + R.string.wallet_quick_affordance_unavailable_configure_the_app ), ) - } else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default() } } @@ -146,6 +147,9 @@ constructor( } } + private fun isWalletAvailable() = + with(walletController.walletClient) { isWalletServiceAvailable && isWalletFeatureAvailable } + private fun state( isFeatureEnabled: Boolean, hasCard: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt index 6f821a2b5228..1ccc689da368 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/VideoCameraQuickAffordanceConfig.kt @@ -62,8 +62,7 @@ constructor( override val key: String get() = BuiltInKeyguardQuickAffordanceKeys.VIDEO_CAMERA - override val pickerName: String - get() = context.getString(R.string.video_camera) + override fun pickerName(): String = context.getString(R.string.video_camera) override val pickerIconResourceId: Int get() = R.drawable.ic_videocam diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt index ab4abbf3d575..34f6b4d74f2b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository import android.content.Context +import android.content.Intent import android.os.UserHandle import android.util.LayoutDirection import com.android.systemui.Dumpable @@ -170,16 +171,27 @@ constructor( pickerState as? KeyguardQuickAffordanceConfig.PickerScreenState.Disabled KeyguardQuickAffordancePickerRepresentation( id = config.key, - name = config.pickerName, + name = config.pickerName(), iconResourceId = config.pickerIconResourceId, isEnabled = pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.Default, - instructions = disabledPickerState?.instructions, + explanation = disabledPickerState?.explanation, actionText = disabledPickerState?.actionText, - actionComponentName = disabledPickerState?.actionComponentName, - configureIntent = defaultPickerState?.configureIntent, + actionIntent = + disabledPickerState?.actionIntent?.apply { + addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + ) + }, + configureIntent = + defaultPickerState?.configureIntent?.apply { + addFlags( + Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + ) + }, ) } + .sortedBy { it.name } } /** @@ -234,7 +246,9 @@ constructor( pw.println(" $slotId$selectionText (capacity = $capacity)") } pw.println("Available affordances on device:") - configs.forEach { config -> pw.println(" ${config.key} (\"${config.pickerName}\")") } + configs.forEach { config -> + pw.println(" ${config.key} (\"${config.pickerName()}\")") + } } } 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 22753376a5d3..ea9c2b28742c 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 @@ -35,6 +35,7 @@ import com.android.systemui.dock.retrieveIsDocked import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry @@ -77,6 +78,7 @@ constructor( private val logger: KeyguardQuickAffordancesMetricsLogger, private val devicePolicyManager: DevicePolicyManager, private val dockManager: DockManager, + private val biometricSettingsRepository: BiometricSettingsRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, @Application private val appContext: Context, ) { @@ -107,9 +109,10 @@ constructor( quickAffordanceAlwaysVisible(position), keyguardInteractor.isDozing, keyguardInteractor.isKeyguardShowing, - keyguardInteractor.isQuickSettingsVisible - ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible -> - if (!isDozing && isKeyguardShowing && !isQuickSettingsVisible) { + keyguardInteractor.isQuickSettingsVisible, + biometricSettingsRepository.isCurrentUserInLockdown, + ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown -> + if (!isDozing && isKeyguardShowing && !isQuickSettingsVisible && !isUserInLockdown) { affordance } else { KeyguardQuickAffordanceModel.Hidden diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt index e7e915940290..c6320dee030a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt @@ -32,8 +32,8 @@ data class KeyguardQuickAffordancePickerRepresentation( /** Whether this quick affordance is enabled. */ val isEnabled: Boolean = true, - /** If not enabled, the list of user-visible steps to re-enable it. */ - val instructions: List<String>? = null, + /** If not enabled, a user-visible explanation as to why. */ + val explanation: String? = null, /** * If not enabled, an optional label for a button that takes the user to a destination where @@ -41,11 +41,8 @@ data class KeyguardQuickAffordancePickerRepresentation( */ val actionText: String? = null, - /** - * If not enabled, an optional component name (package and action) for a button that takes the - * user to a destination where they can re-enable it. - */ - val actionComponentName: String? = null, + /** Optional [Intent] to use to start an activity to re-enable this affordance. */ + val actionIntent: Intent? = null, /** Optional [Intent] to use to start an activity to configure this affordance. */ val configureIntent: Intent? = null, 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 fe62bf4388e9..1c2e85b0fd3a 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,7 @@ package com.android.systemui.keyguard.ui.preview -import android.annotation.ColorInt +import android.app.WallpaperColors import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -47,6 +47,7 @@ import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBind import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel +import com.android.systemui.monet.ColorScheme import com.android.systemui.plugins.ClockController import com.android.systemui.shared.clocks.ClockRegistry import com.android.systemui.shared.clocks.DefaultClockController @@ -91,6 +92,7 @@ constructor( /** [shouldHideClock] here means that we never create and bind the clock views */ private val shouldHideClock: Boolean = bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false) + private val wallpaperColors: WallpaperColors? = bundle.getParcelable(KEY_COLORS) private var host: SurfaceControlViewHost @@ -100,7 +102,6 @@ constructor( private lateinit var largeClockHostView: FrameLayout private lateinit var smallClockHostView: FrameLayout private var smartSpaceView: View? = null - private var colorOverride: Int? = null private val disposables = mutableSetOf<DisposableHandle>() private var isDestroyed = false @@ -172,6 +173,10 @@ constructor( rootView.translationX = (width - scale * rootView.width) / 2 rootView.translationY = (height - scale * rootView.height) / 2 + if (isDestroyed) { + return@post + } + host.setView(rootView, rootView.measuredWidth, rootView.measuredHeight) } } @@ -195,14 +200,6 @@ constructor( mainHandler.post { smartSpaceView?.visibility = if (hide) View.INVISIBLE else View.VISIBLE } } - /** Sets the clock's color to the overridden seed color. */ - fun onColorOverridden(@ColorInt color: Int?) { - mainHandler.post { - colorOverride = color - clockController.clock?.run { events.onSeedColorChanged(color) } - } - } - /** * This sets up and shows a non-interactive smart space * @@ -395,7 +392,23 @@ constructor( val clock = clockRegistry.createCurrentClock() clockController.clock = clock - colorOverride?.let { clock.events.onSeedColorChanged(it) } + if (clockRegistry.seedColor == null) { + // Seed color null means users do override any color on the clock. The default color + // will need to use wallpaper's extracted color and consider if the wallpaper's color + // is dark or a light. + // TODO(b/277832214) we can potentially simplify this code by checking for + // wallpaperColors being null in the if clause above and removing the many ?. + val wallpaperColorScheme = + wallpaperColors?.let { ColorScheme(it, /* darkTheme= */ false) } + val lightClockColor = wallpaperColorScheme?.accent1?.s100 + val darkClockColor = wallpaperColorScheme?.accent2?.s600 + /** Note that when [wallpaperColors] is null, isWallpaperDark is true. */ + val isWallpaperDark: Boolean = + (wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0 + clock.events.onSeedColorChanged( + if (isWallpaperDark) lightClockColor else darkClockColor + ) + } updateLargeClock(clock) updateSmallClock(clock) @@ -428,6 +441,7 @@ constructor( private const val KEY_VIEW_WIDTH = "width" private const val KEY_VIEW_HEIGHT = "height" private const val KEY_DISPLAY_ID = "display_id" + private const val KEY_COLORS = "wallpaper_colors" private const val DIM_ALPHA = 0.3f } 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 79712f9c6be5..dafeacef60bb 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 @@ -124,13 +124,6 @@ constructor( message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE) ) } - KeyguardPreviewConstants.MESSAGE_ID_COLOR_OVERRIDE -> { - renderer.onColorOverridden( - message.data - .getString(KeyguardPreviewConstants.KEY_COLOR_OVERRIDE) - ?.toIntOrNull() - ) - } else -> requestDestruction(this) } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt index c41f82b4ddd1..8d3c6d5d4947 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -38,8 +38,6 @@ import com.android.internal.app.ResolverListController import com.android.internal.app.chooser.NotSelectableTargetInfo import com.android.internal.app.chooser.TargetInfo import com.android.systemui.R -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorComponent import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorResultHandler @@ -53,7 +51,6 @@ import javax.inject.Inject class MediaProjectionAppSelectorActivity( private val componentFactory: MediaProjectionAppSelectorComponent.Factory, private val activityLauncher: AsyncActivityLauncher, - private val featureFlags: FeatureFlags, /** This is used to override the dependency in a screenshot test */ @VisibleForTesting private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)? @@ -62,9 +59,8 @@ class MediaProjectionAppSelectorActivity( @Inject constructor( componentFactory: MediaProjectionAppSelectorComponent.Factory, - activityLauncher: AsyncActivityLauncher, - featureFlags: FeatureFlags - ) : this(componentFactory, activityLauncher, featureFlags, listControllerFactory = null) + activityLauncher: AsyncActivityLauncher + ) : this(componentFactory, activityLauncher, listControllerFactory = null) private lateinit var configurationController: ConfigurationController private lateinit var controller: MediaProjectionAppSelectorController @@ -108,11 +104,7 @@ class MediaProjectionAppSelectorActivity( override fun appliedThemeResId(): Int = R.style.Theme_SystemUI_MediaProjectionAppSelector override fun createBlockerEmptyStateProvider(): EmptyStateProvider = - if (featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) { - component.emptyStateProvider - } else { - object : EmptyStateProvider {} - } + component.emptyStateProvider override fun createListController(userHandle: UserHandle): ResolverListController = listControllerFactory?.invoke(userHandle) ?: super.createListController(userHandle) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt index e38abc2228c4..6eaff3fcb929 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarObserver.kt @@ -56,6 +56,7 @@ open class SeekBarObserver(private val holder: MediaViewHolder) : R.dimen.qs_media_session_disabled_seekbar_vertical_padding ) var seekBarResetAnimator: Animator? = null + var animationEnabled: Boolean = true init { val seekBarProgressWavelength = @@ -104,7 +105,7 @@ open class SeekBarObserver(private val holder: MediaViewHolder) : holder.seekBar.thumb.alpha = if (data.seekAvailable) 255 else 0 holder.seekBar.isEnabled = data.seekAvailable - progressDrawable?.animate = data.playing && !data.scrubbing + progressDrawable?.animate = data.playing && !data.scrubbing && animationEnabled progressDrawable?.transitionEnabled = !data.seekAvailable if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index 32a793556681..0819d0d36e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -34,6 +34,7 @@ import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.BlendMode; import android.graphics.Color; @@ -54,6 +55,7 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Process; import android.os.Trace; +import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -74,7 +76,6 @@ import androidx.constraintlayout.widget.ConstraintSet; import com.android.app.animation.Interpolators; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.ColorUtils; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.InstanceId; import com.android.internal.widget.CachingIconView; @@ -121,6 +122,7 @@ import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseContro import com.android.systemui.util.ColorUtilKt; import com.android.systemui.util.animation.TransitionLayout; import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.time.SystemClock; import dagger.Lazy; @@ -244,10 +246,18 @@ public class MediaControlPanel { private MultiRippleController mMultiRippleController; private TurbulenceNoiseController mTurbulenceNoiseController; private final FeatureFlags mFeatureFlags; + private final GlobalSettings mGlobalSettings; - // TODO(b/281032715): Consider making this as a final variable. For now having a null check - // due to unit test failure. (Perhaps missing some setup) private TurbulenceNoiseAnimationConfig mTurbulenceNoiseAnimationConfig; + private boolean mWasPlaying = false; + private boolean mButtonClicked = false; + + private ContentObserver mAnimationScaleObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + updateAnimatorDurationScale(); + } + }; /** * Initialize a new control panel @@ -276,7 +286,8 @@ public class MediaControlPanel { ActivityIntentHelper activityIntentHelper, NotificationLockscreenUserManager lockscreenUserManager, BroadcastDialogController broadcastDialogController, - FeatureFlags featureFlags + FeatureFlags featureFlags, + GlobalSettings globalSettings ) { mContext = context; mBackgroundExecutor = backgroundExecutor; @@ -305,6 +316,13 @@ public class MediaControlPanel { }); mFeatureFlags = featureFlags; + + mGlobalSettings = globalSettings; + mGlobalSettings.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE), + mAnimationScaleObserver + ); + updateAnimatorDurationScale(); } /** @@ -387,6 +405,13 @@ public class MediaControlPanel { updateSeekBarVisibility(); } + private void updateAnimatorDurationScale() { + if (mSeekBarObserver != null) { + mSeekBarObserver.setAnimationEnabled( + mGlobalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f); + } + } + /** * Get the context * @@ -557,6 +582,25 @@ public class MediaControlPanel { if (!mMetadataAnimationHandler.isRunning()) { mMediaViewController.refreshState(); } + + // Turbulence noise + if (shouldPlayTurbulenceNoise()) { + if (mTurbulenceNoiseAnimationConfig == null) { + mTurbulenceNoiseAnimationConfig = + createTurbulenceNoiseAnimation(); + } + // Color will be correctly updated in ColorSchemeTransition. + mTurbulenceNoiseController.play( + mTurbulenceNoiseAnimationConfig + ); + mMainExecutor.executeDelayed( + mTurbulenceNoiseController::finish, + TURBULENCE_NOISE_PLAY_DURATION + ); + } + mButtonClicked = false; + mWasPlaying = isPlaying(); + Trace.endSection(); } @@ -1130,21 +1174,14 @@ public class MediaControlPanel { if (!mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) { mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId); logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); + // Used to determine whether to play turbulence noise. + mWasPlaying = isPlaying(); + mButtonClicked = true; + action.run(); + if (mFeatureFlags.isEnabled(Flags.UMO_SURFACE_RIPPLE)) { mMultiRippleController.play(createTouchRippleAnimation(button)); - if (mFeatureFlags.isEnabled(Flags.UMO_TURBULENCE_NOISE)) { - if (mTurbulenceNoiseAnimationConfig == null) { - mTurbulenceNoiseAnimationConfig = - createTurbulenceNoiseAnimation(); - } - // Color will be correctly updated in ColorSchemeTransition. - mTurbulenceNoiseController.play(mTurbulenceNoiseAnimationConfig); - mMainExecutor.executeDelayed( - mTurbulenceNoiseController::finish, - TURBULENCE_NOISE_PLAY_DURATION - ); - } } if (icon instanceof Animatable) { @@ -1183,26 +1220,31 @@ public class MediaControlPanel { ); } + private boolean shouldPlayTurbulenceNoise() { + return mFeatureFlags.isEnabled(Flags.UMO_TURBULENCE_NOISE) && mButtonClicked && !mWasPlaying + && isPlaying(); + } + private TurbulenceNoiseAnimationConfig createTurbulenceNoiseAnimation() { return new TurbulenceNoiseAnimationConfig( - TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_GRID_COUNT, + /* gridCount= */ 2.14f, TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER, - /* noiseMoveSpeedX= */ 0f, + /* noiseMoveSpeedX= */ 0.42f, /* noiseMoveSpeedY= */ 0f, TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(), - // We want to add (BlendMode.PLUS) the turbulence noise on top of the album art. - // Thus, set the background color with alpha 0. - /* backgroundColor= */ ColorUtils.setAlphaComponent(Color.BLACK, 0), - TurbulenceNoiseAnimationConfig.DEFAULT_OPACITY, - /* width= */ mMediaViewHolder.getMultiRippleView().getWidth(), - /* height= */ mMediaViewHolder.getMultiRippleView().getHeight(), + /* backgroundColor= */ Color.BLACK, + /* opacity= */ 51, + /* width= */ mMediaViewHolder.getTurbulenceNoiseView().getWidth(), + /* height= */ mMediaViewHolder.getTurbulenceNoiseView().getHeight(), TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, - /* easeInDuration= */ 2500f, - /* easeOutDuration= */ 2500f, - this.getContext().getResources().getDisplayMetrics().density, - BlendMode.PLUS, - /* onAnimationEnd= */ null + /* easeInDuration= */ 1350f, + /* easeOutDuration= */ 1350f, + getContext().getResources().getDisplayMetrics().density, + BlendMode.SCREEN, + /* onAnimationEnd= */ null, + /* lumaMatteBlendFactor= */ 0.26f, + /* lumaMatteOverallBrightness= */ 0.09f ); } private void clearButton(final ImageButton button) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 316c903eed5b..88ffa8da666b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -28,6 +28,7 @@ import android.os.Build; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.CheckBox; import android.widget.TextView; @@ -151,6 +152,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mCurrentActivePosition = -1; } mStatusIcon.setVisibility(View.GONE); + enableFocusPropertyForView(mContainerLayout); if (mController.isAnyDeviceTransferring()) { if (device.getState() == MediaDeviceState.STATE_CONNECTING @@ -250,7 +252,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mController.getColorItemContent()); updateGroupableCheckBox(true, isDeviceDeselectable, device); updateEndClickArea(device, isDeviceDeselectable); - setUpContentDescriptionForView(mContainerLayout, false, device); + disableFocusPropertyForView(mContainerLayout); + setUpContentDescriptionForView(mSeekBar, device); setSingleLineLayout(getItemTitle(device), true /* showSeekBar */, false /* showProgressBar */, true /* showCheckBox */, true /* showEndTouchArea */); @@ -274,7 +277,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mController.getDeselectableMediaDevice(), device); updateGroupableCheckBox(true, isDeviceDeselectable, device); updateEndClickArea(device, isDeviceDeselectable); - setUpContentDescriptionForView(mContainerLayout, false, device); + disableFocusPropertyForView(mContainerLayout); + setUpContentDescriptionForView(mSeekBar, device); setSingleLineLayout(getItemTitle(device), true /* showSeekBar */, false /* showProgressBar */, true /* showCheckBox */, true /* showEndTouchArea */); @@ -282,7 +286,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } else { updateTitleIcon(R.drawable.media_output_icon_volume, mController.getColorItemContent()); - setUpContentDescriptionForView(mContainerLayout, false, device); + disableFocusPropertyForView(mContainerLayout); + setUpContentDescriptionForView(mSeekBar, device); mCurrentActivePosition = position; setSingleLineLayout(getItemTitle(device), true /* showSeekBar */, false /* showProgressBar */, false /* showCheckBox */, @@ -390,7 +395,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { View.IMPORTANT_FOR_ACCESSIBILITY_YES); mEndTouchArea.setBackgroundTintList( ColorStateList.valueOf(mController.getColorItemBackground())); - setUpContentDescriptionForView(mEndTouchArea, true, device); + setUpContentDescriptionForView(mEndTouchArea, device); } private void updateGroupableCheckBox(boolean isSelected, boolean isGroupable, @@ -471,14 +476,29 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { notifyDataSetChanged(); } - private void setUpContentDescriptionForView(View view, boolean clickable, - MediaDevice device) { - view.setClickable(clickable); + private void disableFocusPropertyForView(View view) { + view.setFocusable(false); + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } + + private void enableFocusPropertyForView(View view) { + view.setFocusable(true); + view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } + + private void setUpContentDescriptionForView(View view, MediaDevice device) { view.setContentDescription( mContext.getString(device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE ? R.string.accessibility_bluetooth_name : R.string.accessibility_cast_name, device.getName())); + view.setAccessibilityDelegate(new View.AccessibilityDelegate() { + public void onInitializeAccessibilityNodeInfo(View host, + AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(host, info); + host.setOnClickListener(null); + } + }); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java index abf0932c8407..b4578e97eda2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java @@ -41,6 +41,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.graphics.drawable.IconCompat; +import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.media.BluetoothMediaDevice; import com.android.settingslib.media.MediaDevice; import com.android.settingslib.qrcode.QrCodeGenerator; @@ -58,6 +59,17 @@ import com.google.zxing.WriterException; public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { private static final String TAG = "MediaOutputBroadcastDialog"; + static final int METADATA_BROADCAST_NAME = 0; + static final int METADATA_BROADCAST_CODE = 1; + + private static final int MAX_BROADCAST_INFO_UPDATE = 3; + @VisibleForTesting + static final int BROADCAST_CODE_MAX_LENGTH = 16; + @VisibleForTesting + static final int BROADCAST_CODE_MIN_LENGTH = 4; + @VisibleForTesting + static final int BROADCAST_NAME_MAX_LENGTH = 254; + private ViewStub mBroadcastInfoArea; private ImageView mBroadcastQrCodeView; private ImageView mBroadcastNotify; @@ -67,14 +79,16 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { private ImageView mBroadcastCodeEye; private Boolean mIsPasswordHide = true; private ImageView mBroadcastCodeEdit; - private AlertDialog mAlertDialog; + @VisibleForTesting + AlertDialog mAlertDialog; private TextView mBroadcastErrorMessage; private int mRetryCount = 0; private String mCurrentBroadcastName; private String mCurrentBroadcastCode; private boolean mIsStopbyUpdateBroadcastCode = false; + private boolean mIsLeBroadcastAssistantCallbackRegistered; - private TextWatcher mTextWatcher = new TextWatcher() { + private TextWatcher mBroadcastCodeTextWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // Do nothing @@ -102,7 +116,9 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { R.string.media_output_broadcast_code_hint_no_less_than_min); } else if (breakBroadcastCodeRuleTextLengthMoreThanMax) { mBroadcastErrorMessage.setText( - R.string.media_output_broadcast_code_hint_no_more_than_max); + mContext.getResources().getString( + R.string.media_output_broadcast_edit_hint_no_more_than_max, + BROADCAST_CODE_MAX_LENGTH)); } mBroadcastErrorMessage.setVisibility(breakRule ? View.VISIBLE : View.INVISIBLE); @@ -113,7 +129,40 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { } }; - private boolean mIsLeBroadcastAssistantCallbackRegistered; + private TextWatcher mBroadcastNameTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + @Override + public void afterTextChanged(Editable s) { + if (mAlertDialog == null || mBroadcastErrorMessage == null) { + return; + } + boolean breakBroadcastNameRuleTextLengthMoreThanMax = + s.length() > BROADCAST_NAME_MAX_LENGTH; + boolean breakRule = breakBroadcastNameRuleTextLengthMoreThanMax || (s.length() == 0); + + if (breakBroadcastNameRuleTextLengthMoreThanMax) { + mBroadcastErrorMessage.setText( + mContext.getResources().getString( + R.string.media_output_broadcast_edit_hint_no_more_than_max, + BROADCAST_NAME_MAX_LENGTH)); + } + mBroadcastErrorMessage.setVisibility( + breakBroadcastNameRuleTextLengthMoreThanMax ? View.VISIBLE : View.INVISIBLE); + Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + if (positiveBtn != null) { + positiveBtn.setEnabled(breakRule ? false : true); + } + } + }; private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback = new BluetoothLeBroadcastAssistant.Callback() { @@ -186,13 +235,6 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { } }; - static final int METADATA_BROADCAST_NAME = 0; - static final int METADATA_BROADCAST_CODE = 1; - - private static final int MAX_BROADCAST_INFO_UPDATE = 3; - private static final int BROADCAST_CODE_MAX_LENGTH = 16; - private static final int BROADCAST_CODE_MIN_LENGTH = 4; - MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender, MediaOutputController mediaOutputController) { super(context, broadcastSender, mediaOutputController); @@ -391,13 +433,12 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { R.layout.media_output_broadcast_update_dialog, null); final EditText editText = layout.requireViewById(R.id.broadcast_edit_text); editText.setText(editString); - if (isBroadcastCode) { - editText.addTextChangedListener(mTextWatcher); - } + editText.addTextChangedListener( + isBroadcastCode ? mBroadcastCodeTextWatcher : mBroadcastNameTextWatcher); mBroadcastErrorMessage = layout.requireViewById(R.id.broadcast_error_message); mAlertDialog = new Builder(mContext) .setTitle(isBroadcastCode ? R.string.media_output_broadcast_code - : R.string.media_output_broadcast_name) + : R.string.media_output_broadcast_name) .setView(layout) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(R.string.media_output_broadcast_dialog_save, @@ -420,7 +461,8 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { return mMediaOutputController.getBroadcastMetadata(); } - private void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) { + @VisibleForTesting + void updateBroadcastInfo(boolean isBroadcastCode, String updatedString) { Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); if (positiveBtn != null) { positiveBtn.setEnabled(false); @@ -523,16 +565,33 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { } private void handleUpdateFailedUi() { - final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); - mBroadcastErrorMessage.setVisibility(View.VISIBLE); + if (mAlertDialog == null) { + Log.d(TAG, "handleUpdateFailedUi: mAlertDialog is null"); + return; + } + int errorMessageStringId = -1; + boolean enablePositiveBtn = false; if (mRetryCount < MAX_BROADCAST_INFO_UPDATE) { - if (positiveBtn != null) { - positiveBtn.setEnabled(true); - } - mBroadcastErrorMessage.setText(R.string.media_output_broadcast_update_error); + enablePositiveBtn = true; + errorMessageStringId = R.string.media_output_broadcast_update_error; } else { mRetryCount = 0; - mBroadcastErrorMessage.setText(R.string.media_output_broadcast_last_update_error); + errorMessageStringId = R.string.media_output_broadcast_last_update_error; } + + // update UI + final Button positiveBtn = mAlertDialog.getButton(AlertDialog.BUTTON_POSITIVE); + if (positiveBtn != null && enablePositiveBtn) { + positiveBtn.setEnabled(true); + } + if (mBroadcastErrorMessage != null) { + mBroadcastErrorMessage.setVisibility(View.VISIBLE); + mBroadcastErrorMessage.setText(errorMessageStringId); + } + } + + @VisibleForTesting + int getRetryCount() { + return mRetryCount; } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt index 1d8fe729494d..e61650fbb163 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt @@ -18,9 +18,9 @@ package com.android.systemui.mediaprojection.appselector import android.content.ComponentName import android.os.UserHandle -import com.android.systemui.flags.FeatureFlags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel @@ -32,7 +32,7 @@ class MediaProjectionAppSelectorController constructor( private val recentTaskListProvider: RecentTaskListProvider, private val view: MediaProjectionAppSelectorView, - private val flags: FeatureFlags, + private val devicePolicyResolver: ScreenCaptureDevicePolicyResolver, @HostUserHandle private val hostUserHandle: UserHandle, @MediaProjectionAppSelector private val scope: CoroutineScope, @MediaProjectionAppSelector private val appSelectorComponentName: ComponentName, @@ -54,11 +54,13 @@ constructor( scope.cancel() } - /** - * Removes all recent tasks that are different from the profile of the host app to avoid any - * cross-profile sharing - */ - private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = this + /** Removes all recent tasks that should be blocked according to the policy */ + private fun List<RecentTask>.filterDevicePolicyRestrictedTasks(): List<RecentTask> = filter { + devicePolicyResolver.isScreenCaptureAllowed( + targetAppUserHandle = UserHandle.of(it.userId), + hostAppUserHandle = hostUserHandle + ) + } private fun List<RecentTask>.filterAppSelector(): List<RecentTask> = filter { // Only take tasks that is not the app selector diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt index 6bd33e7e5c97..b11a244b2e3e 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt @@ -47,7 +47,7 @@ constructor( return false } - if (!hostAppUserHandle.isWorkProfile() && personalProfileScreenCaptureDisabled) { + if (personalProfileScreenCaptureDisabled) { // Disable screen capturing as personal apps should not capture the screen return false } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index b0fb349083e6..682335e0b419 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -76,7 +76,6 @@ import android.telecom.TelecomManager; import android.text.TextUtils; import android.util.Log; import android.view.Display; -import android.view.DisplayCutout; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.InsetsFrameProvider; @@ -1730,9 +1729,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements tappableElementProvider.setInsetsSize(Insets.NONE); } - final DisplayCutout cutout = userContext.getDisplay().getCutout(); - final int safeInsetsLeft = cutout != null ? cutout.getSafeInsetLeft() : 0; - final int safeInsetsRight = cutout != null ? cutout.getSafeInsetRight() : 0; final int gestureHeight = userContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.navigation_bar_gesture_height); final boolean handlingGesture = mEdgeBackGestureHandler.isHandlingGestures(); @@ -1742,19 +1738,23 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mandatoryGestureProvider.setInsetsSize(Insets.of(0, 0, 0, gestureHeight)); } final int gestureInsetsLeft = handlingGesture - ? mEdgeBackGestureHandler.getEdgeWidthLeft() + safeInsetsLeft : 0; + ? mEdgeBackGestureHandler.getEdgeWidthLeft() : 0; final int gestureInsetsRight = handlingGesture - ? mEdgeBackGestureHandler.getEdgeWidthRight() + safeInsetsRight : 0; + ? mEdgeBackGestureHandler.getEdgeWidthRight() : 0; return new InsetsFrameProvider[] { navBarProvider, tappableElementProvider, mandatoryGestureProvider, new InsetsFrameProvider(mInsetsSourceOwner, 0, WindowInsets.Type.systemGestures()) .setSource(InsetsFrameProvider.SOURCE_DISPLAY) - .setInsetsSize(Insets.of(gestureInsetsLeft, 0, 0, 0)), + .setInsetsSize(Insets.of(gestureInsetsLeft, 0, 0, 0)) + .setMinimalInsetsSizeInDisplayCutoutSafe( + Insets.of(gestureInsetsLeft, 0, 0, 0)), new InsetsFrameProvider(mInsetsSourceOwner, 1, WindowInsets.Type.systemGestures()) .setSource(InsetsFrameProvider.SOURCE_DISPLAY) .setInsetsSize(Insets.of(0, 0, gestureInsetsRight, 0)) + .setMinimalInsetsSizeInDisplayCutoutSafe( + Insets.of(0, 0, gestureInsetsRight, 0)) }; } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt index c804df8fa555..a256b59ac076 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt @@ -157,17 +157,17 @@ class BackPanel( arrowPaint.color = Utils.getColorAttrDefaultColor(context, if (isDeviceInNightTheme) { - com.android.internal.R.attr.colorAccentPrimary + com.android.internal.R.attr.materialColorOnSecondaryContainer } else { - com.android.internal.R.attr.textColorPrimary + com.android.internal.R.attr.materialColorOnSecondaryFixed } ) arrowBackgroundPaint.color = Utils.getColorAttrDefaultColor(context, if (isDeviceInNightTheme) { - com.android.internal.R.attr.materialColorOnSecondary + com.android.internal.R.attr.materialColorSecondaryContainer } else { - com.android.internal.R.attr.colorAccentSecondary + com.android.internal.R.attr.materialColorSecondaryFixedDim } ) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index e52418912e1f..8b3d7a653232 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -15,8 +15,6 @@ */ package com.android.systemui.navigationbar.gestural; -import static android.view.InputDevice.SOURCE_MOUSE; -import static android.view.InputDevice.SOURCE_TOUCHPAD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static com.android.systemui.classifier.Classifier.BACK_GESTURE; @@ -951,12 +949,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mMLResults = 0; mLogGesture = false; mInRejectedExclusion = false; - // Trackpad back gestures don't have zones, so we don't need to check if the down event - // is within insets. Also we don't allow back for button press from the trackpad, and - // yet we do with a mouse. boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); + // Trackpad back gestures don't have zones, so we don't need to check if the down event + // is within insets. mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed - && !isButtonPressFromTrackpad(ev) && (isTrackpadMultiFingerSwipe || isWithinInsets) && !mGestureBlockingActivityRunning && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) @@ -1069,11 +1065,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.scheduleFrameUpdate(); } - private boolean isButtonPressFromTrackpad(MotionEvent ev) { - int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources(); - return (sources & (SOURCE_MOUSE | SOURCE_TOUCHPAD)) == sources && ev.getButtonState() != 0; - } - private void dispatchToBackAnimation(MotionEvent event) { if (mBackAnimation != null) { mVelocityTracker.addMovement(event); diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index 25272ae097a1..ccfbaf1da7b6 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -215,7 +215,7 @@ constructor( debugLog { "onShowNoteTask - opened as app bubble: $info" } } is NoteTaskLaunchMode.Activity -> { - if (activityManager.isInForeground(info.packageName)) { + if (info.isKeyguardLocked && activityManager.isInForeground(info.packageName)) { // Force note task into background by calling home. val intent = createHomeIntent() context.startActivityAsUser(intent, user) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt index fae325cc3147..442000281862 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt @@ -25,12 +25,14 @@ import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity * An entry point represents where the note task has ben called from. In rare cases, it may * represent a "re-entry" (i.e., [APP_CLIPS]). */ -enum class -NoteTaskEntryPoint { +enum class NoteTaskEntryPoint { /** @see [LaunchNoteTaskActivity] */ WIDGET_PICKER_SHORTCUT, + /** @see [LaunchNoteTaskActivity] */ + WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE, + /** @see [NoteTaskQuickAffordanceConfig] */ QUICK_AFFORDANCE, diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt index 48a5933a6030..a79057e5464b 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt @@ -22,6 +22,8 @@ import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON @@ -41,40 +43,45 @@ class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEvent /** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskOpened(info: NoteTaskInfo) { val event = - when (info.entryPoint) { - TAIL_BUTTON -> { - if (info.isKeyguardLocked) { - NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED - } else { - NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON + when (info.entryPoint) { + TAIL_BUTTON -> { + if (info.isKeyguardLocked) { + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED + } else { + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON + } } + + WIDGET_PICKER_SHORTCUT, + WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE -> NOTE_OPENED_VIA_SHORTCUT + + QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE + APP_CLIPS, + KEYBOARD_SHORTCUT, + null -> return } - WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT - QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE - APP_CLIPS -> return - KEYBOARD_SHORTCUT -> return - null -> return - } uiEventLogger.log(event, info.uid, info.packageName) } /** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */ fun logNoteTaskClosed(info: NoteTaskInfo) { val event = - when (info.entryPoint) { - TAIL_BUTTON -> { - if (info.isKeyguardLocked) { - NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED - } else { - NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON + when (info.entryPoint) { + TAIL_BUTTON -> { + if (info.isKeyguardLocked) { + NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED + } else { + NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON + } } + + WIDGET_PICKER_SHORTCUT, + WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE, + QUICK_AFFORDANCE, + APP_CLIPS, + KEYBOARD_SHORTCUT, + null -> return } - WIDGET_PICKER_SHORTCUT -> return - QUICK_AFFORDANCE -> return - APP_CLIPS -> return - KEYBOARD_SHORTCUT -> return - null -> return - } uiEventLogger.log(event, info.uid, info.packageName) } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt index a75834760d30..269eb870686c 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt @@ -16,6 +16,7 @@ package com.android.systemui.notetask import android.os.UserHandle +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE /** Contextual information required to launch a Note Task by [NoteTaskController]. */ data class NoteTaskInfo( @@ -27,7 +28,7 @@ data class NoteTaskInfo( ) { val launchMode: NoteTaskLaunchMode = - if (isKeyguardLocked) { + if (isKeyguardLocked || entryPoint == WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) { NoteTaskLaunchMode.Activity } else { NoteTaskLaunchMode.AppBubble diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt index 441b9f5d0181..754c3650a5ed 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt @@ -51,7 +51,8 @@ internal object NoteTaskRoleManagerExt { val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget) return ShortcutInfo.Builder(context, NoteTaskController.SHORTCUT_ID) - .setIntent(LaunchNoteTaskActivity.newIntent(context = context)) + .setIntent(LaunchNoteTaskActivity.createIntent(context)) + .setActivity(LaunchNoteTaskActivity.createComponent(context)) .setShortLabel(context.getString(R.string.note_task_button_label)) .setLongLived(true) .setIcon(icon) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 444407c2341a..f02d362501d5 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -19,6 +19,7 @@ package com.android.systemui.notetask.quickaffordance import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager import android.content.Context +import android.content.Intent import android.hardware.input.InputSettings import android.os.Build import android.os.UserHandle @@ -42,7 +43,6 @@ import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEnabledKey import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE import com.android.systemui.notetask.NoteTaskInfoResolver -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import dagger.Lazy import java.util.concurrent.Executor @@ -73,7 +73,7 @@ constructor( private val pickerNameResourceId = R.string.note_task_button_label - override val pickerName: String = context.getString(pickerNameResourceId) + override fun pickerName(): String = context.getString(pickerNameResourceId) override val pickerIconResourceId = R.drawable.ic_note_task_shortcut_keyguard @@ -122,16 +122,18 @@ constructor( isEnabled && isDefaultNotesAppSet -> PickerScreenState.Default() isEnabled -> { PickerScreenState.Disabled( - listOf( + explanation = context.getString( - R.string.keyguard_affordance_enablement_dialog_notes_app_instruction - ) - ), - context.getString( - R.string.keyguard_affordance_enablement_dialog_notes_app_action - ), - "${context.packageName}$COMPONENT_NAME_SEPARATOR" + - "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE", + R.string.notes_app_quick_affordance_unavailable_explanation + ), + actionText = + context.getString( + R.string.keyguard_affordance_enablement_dialog_notes_app_action + ), + actionIntent = + Intent(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE).apply { + setPackage(context.packageName) + }, ) } else -> PickerScreenState.UnavailableOnDevice diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt index 8ca13b9776bb..7ef149de2794 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.notetask.shortcut +import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Bundle @@ -72,7 +73,13 @@ constructor( controller.startNoteTaskProxyActivityForUser(mainUser) } } else { - controller.showNoteTask(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT) + val entryPoint = + if (isInMultiWindowMode) { + NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE + } else { + NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT + } + controller.showNoteTask(entryPoint) } finish() } @@ -80,11 +87,14 @@ constructor( companion object { /** Creates a new [Intent] set to start [LaunchNoteTaskActivity]. */ - fun newIntent(context: Context): Intent { - return Intent(context, LaunchNoteTaskActivity::class.java).apply { + fun createIntent(context: Context): Intent = + Intent(context, LaunchNoteTaskActivity::class.java).apply { // Intent's action must be set in shortcuts, or an exception will be thrown. action = Intent.ACTION_CREATE_NOTE } - } + + /** Creates a new [ComponentName] for [LaunchNoteTaskActivity]. */ + fun createComponent(context: Context): ComponentName = + ComponentName(context, LaunchNoteTaskActivity::class.java) } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 18be0aa6580d..8d3b7451c90b 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -775,9 +775,13 @@ public class PeopleSpaceWidgetManager { NotificationChannel channel, int modificationType) { if (channel.isConversation()) { - updateWidgets(mAppWidgetManager.getAppWidgetIds( - new ComponentName(mContext, PeopleSpaceWidgetProvider.class) - )); + mBgExecutor.execute(() -> { + if (mUserManager.isUserUnlocked(user)) { + updateWidgets(mAppWidgetManager.getAppWidgetIds( + new ComponentName(mContext, PeopleSpaceWidgetProvider.class) + )); + } + }); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 897b0e73dca0..5d028307a62d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -65,14 +65,12 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.DisplayTracker; -import dagger.Lazy; - import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; - +import dagger.Lazy; public class CustomTile extends QSTileImpl<State> implements TileChangeListener { public static final String PREFIX = "custom("; diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt index a066242fd96b..d7ae575724dd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt @@ -20,6 +20,8 @@ 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.qs.pipeline.data.repository.InstalledTilesComponentRepository +import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepositoryImpl import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecSettingsRepository import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor @@ -45,6 +47,11 @@ abstract class QSPipelineModule { ): CurrentTilesInteractor @Binds + abstract fun provideInstalledTilesPackageRepository( + impl: InstalledTilesComponentRepositoryImpl + ): InstalledTilesComponentRepository + + @Binds @IntoMap @ClassKey(PrototypeCoreStartable::class) abstract fun providePrototypeCoreStartable(startable: PrototypeCoreStartable): CoreStartable diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt new file mode 100644 index 000000000000..498f403e8c7a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.repository + +import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE +import android.annotation.WorkerThread +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ResolveInfoFlags +import android.os.UserHandle +import android.service.quicksettings.TileService +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.kotlin.isComponentActuallyEnabled +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart + +interface InstalledTilesComponentRepository { + + fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> +} + +@SysUISingleton +class InstalledTilesComponentRepositoryImpl +@Inject +constructor( + @Application private val applicationContext: Context, + private val packageManager: PackageManager, + @Background private val backgroundDispatcher: CoroutineDispatcher, +) : InstalledTilesComponentRepository { + + override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> = + conflatedCallbackFlow { + val receiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + trySend(Unit) + } + } + applicationContext.registerReceiverAsUser( + receiver, + UserHandle.of(userId), + INTENT_FILTER, + /* broadcastPermission = */ null, + /* scheduler = */ null + ) + + awaitClose { applicationContext.unregisterReceiver(receiver) } + } + .onStart { emit(Unit) } + .map { reloadComponents(userId) } + .distinctUntilChanged() + .flowOn(backgroundDispatcher) + + @WorkerThread + private fun reloadComponents(userId: Int): Set<ComponentName> { + return packageManager + .queryIntentServicesAsUser(INTENT, FLAGS, userId) + .mapNotNull { it.serviceInfo } + .filter { it.permission == BIND_QUICK_SETTINGS_TILE } + .filter { packageManager.isComponentActuallyEnabled(it) } + .mapTo(mutableSetOf()) { it.componentName } + } + + companion object { + private val INTENT_FILTER = + IntentFilter().apply { + addAction(Intent.ACTION_PACKAGE_ADDED) + addAction(Intent.ACTION_PACKAGE_CHANGED) + addAction(Intent.ACTION_PACKAGE_REMOVED) + addAction(Intent.ACTION_PACKAGE_REPLACED) + addDataScheme("package") + } + private val INTENT = Intent(TileService.ACTION_QS_TILE) + private val FLAGS = + ResolveInfoFlags.of( + (PackageManager.GET_SERVICES or + PackageManager.MATCH_DIRECT_BOOT_AWARE or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE) + .toLong() + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt index 3b2362f2b326..a162d113a3b2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt @@ -42,6 +42,8 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** Repository that tracks the current tiles. */ @@ -104,6 +106,8 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, ) : TileSpecRepository { + private val mutex = Mutex() + private val retailModeTiles by lazy { resources .getString(R.string.quick_settings_tiles_retail_mode) @@ -145,37 +149,40 @@ constructor( .flowOn(backgroundDispatcher) } - override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) { - if (tile == TileSpec.Invalid) { - return - } - val tilesList = loadTiles(userId).toMutableList() - if (tile !in tilesList) { - if (position < 0 || position >= tilesList.size) { - tilesList.add(tile) - } else { - tilesList.add(position, tile) + override suspend fun addTile(userId: Int, tile: TileSpec, position: Int) = + mutex.withLock { + if (tile == TileSpec.Invalid) { + return + } + val tilesList = loadTiles(userId).toMutableList() + if (tile !in tilesList) { + if (position < 0 || position >= tilesList.size) { + tilesList.add(tile) + } else { + tilesList.add(position, tile) + } + storeTiles(userId, tilesList) } - storeTiles(userId, tilesList) } - } - override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) { - if (tiles.all { it == TileSpec.Invalid }) { - return - } - val tilesList = loadTiles(userId).toMutableList() - if (tilesList.removeAll(tiles)) { - storeTiles(userId, tilesList.toList()) + override suspend fun removeTiles(userId: Int, tiles: Collection<TileSpec>) = + mutex.withLock { + if (tiles.all { it == TileSpec.Invalid }) { + return + } + val tilesList = loadTiles(userId).toMutableList() + if (tilesList.removeAll(tiles)) { + storeTiles(userId, tilesList.toList()) + } } - } - override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) { - val filtered = tiles.filter { it != TileSpec.Invalid } - if (filtered.isNotEmpty()) { - storeTiles(userId, filtered) + override suspend fun setTiles(userId: Int, tiles: List<TileSpec>) = + mutex.withLock { + val filtered = tiles.filter { it != TileSpec.Invalid } + if (filtered.isNotEmpty()) { + storeTiles(userId, filtered) + } } - } private suspend fun loadTiles(@UserIdInt forUser: Int): List<TileSpec> { return withContext(backgroundDispatcher) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index c579f5c3061c..ff881f767b87 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -36,6 +36,7 @@ import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.TileLifecycleManager import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository +import com.android.systemui.qs.pipeline.data.repository.InstalledTilesComponentRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.domain.model.TileModel import com.android.systemui.qs.pipeline.shared.TileSpec @@ -52,6 +53,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn @@ -117,11 +120,13 @@ interface CurrentTilesInteractor : ProtoDumpable { * * Platform tiles will be kept between users, with a call to [QSTile.userSwitch] * * [CustomTile]s will only be destroyed if the user changes. */ +@OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class CurrentTilesInteractorImpl @Inject constructor( private val tileSpecRepository: TileSpecRepository, + private val installedTilesComponentRepository: InstalledTilesComponentRepository, private val userRepository: UserRepository, private val customTileStatePersister: CustomTileStatePersister, private val tileFactory: QSFactory, @@ -141,7 +146,7 @@ constructor( override val currentTiles: StateFlow<List<TileModel>> = _currentSpecsAndTiles.asStateFlow() // This variable should only be accessed inside the collect of `startTileCollection`. - private val specsToTiles = mutableMapOf<TileSpec, QSTile>() + private val specsToTiles = mutableMapOf<TileSpec, TileOrNotInstalled>() private val currentUser = MutableStateFlow(userTracker.userId) override val userId = currentUser.asStateFlow() @@ -149,6 +154,20 @@ constructor( private val _userContext = MutableStateFlow(userTracker.userContext) override val userContext = _userContext.asStateFlow() + private val userAndTiles = + currentUser + .flatMapLatest { userId -> + tileSpecRepository.tilesSpecs(userId).map { UserAndTiles(userId, it) } + } + .distinctUntilChanged() + .pairwise(UserAndTiles(-1, emptyList())) + .flowOn(backgroundDispatcher) + + private val installedPackagesWithTiles = + currentUser.flatMapLatest { + installedTilesComponentRepository.getInstalledTilesComponents(it) + } + init { if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) { startTileCollection() @@ -158,68 +177,98 @@ constructor( @OptIn(ExperimentalCoroutinesApi::class) private fun startTileCollection() { scope.launch { - userRepository.selectedUserInfo - .flatMapLatest { user -> + launch { + userRepository.selectedUserInfo.collect { user -> currentUser.value = user.id _userContext.value = userTracker.userContext - tileSpecRepository.tilesSpecs(user.id).map { user.id to it } } - .distinctUntilChanged() - .pairwise(-1 to emptyList()) - .flowOn(backgroundDispatcher) - .collect { (old, new) -> - val newTileList = new.second - val userChanged = old.first != new.first - val newUser = new.first - - // Destroy all tiles that are not in the new set - specsToTiles - .filter { it.key !in newTileList } - .forEach { entry -> - logger.logTileDestroyed( - entry.key, - if (userChanged) { - QSPipelineLogger.TileDestroyedReason - .TILE_NOT_PRESENT_IN_NEW_USER - } else { - QSPipelineLogger.TileDestroyedReason.TILE_REMOVED - } - ) - entry.value.destroy() - } - // MutableMap will keep the insertion order - val newTileMap = mutableMapOf<TileSpec, QSTile>() - - newTileList.forEach { tileSpec -> - if (tileSpec !in newTileMap) { - val newTile = - if (tileSpec in specsToTiles) { - processExistingTile( - tileSpec, - specsToTiles.getValue(tileSpec), - userChanged, - newUser - ) - ?: createTile(tileSpec) + } + + launch(backgroundDispatcher) { + userAndTiles + .combine(installedPackagesWithTiles) { usersAndTiles, packages -> + Data( + usersAndTiles.previousValue, + usersAndTiles.newValue, + packages, + ) + } + .collectLatest { + val newTileList = it.newData.tiles + val userChanged = it.oldData.userId != it.newData.userId + val newUser = it.newData.userId + val components = it.installedComponents + + // Destroy all tiles that are not in the new set + specsToTiles + .filter { + it.key !in newTileList && it.value is TileOrNotInstalled.Tile + } + .forEach { entry -> + logger.logTileDestroyed( + entry.key, + if (userChanged) { + QSPipelineLogger.TileDestroyedReason + .TILE_NOT_PRESENT_IN_NEW_USER + } else { + QSPipelineLogger.TileDestroyedReason.TILE_REMOVED + } + ) + (entry.value as TileOrNotInstalled.Tile).tile.destroy() + } + // MutableMap will keep the insertion order + val newTileMap = mutableMapOf<TileSpec, TileOrNotInstalled>() + + newTileList.forEach { tileSpec -> + if (tileSpec !in newTileMap) { + if ( + tileSpec is TileSpec.CustomTileSpec && + tileSpec.componentName !in components + ) { + newTileMap[tileSpec] = TileOrNotInstalled.NotInstalled } else { - createTile(tileSpec) + // Create tile here will never try to create a CustomTile that + // is not installed + val newTile = + if (tileSpec in specsToTiles) { + processExistingTile( + tileSpec, + specsToTiles.getValue(tileSpec), + userChanged, + newUser + ) + ?: createTile(tileSpec) + } else { + createTile(tileSpec) + } + if (newTile != null) { + newTileMap[tileSpec] = TileOrNotInstalled.Tile(newTile) + } } - if (newTile != null) { - newTileMap[tileSpec] = newTile } } - } - val resolvedSpecs = newTileMap.keys.toList() - specsToTiles.clear() - specsToTiles.putAll(newTileMap) - _currentSpecsAndTiles.value = newTileMap.map { TileModel(it.key, it.value) } - if (resolvedSpecs != newTileList) { - // There were some tiles that couldn't be created. Change the value in the - // repository - launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } + val resolvedSpecs = newTileMap.keys.toList() + specsToTiles.clear() + specsToTiles.putAll(newTileMap) + _currentSpecsAndTiles.value = + newTileMap + .filter { it.value is TileOrNotInstalled.Tile } + .map { + TileModel(it.key, (it.value as TileOrNotInstalled.Tile).tile) + } + logger.logTilesNotInstalled( + newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys, + newUser + ) + if (resolvedSpecs != newTileList) { + // There were some tiles that couldn't be created. Change the value in + // the + // repository + launch { tileSpecRepository.setTiles(currentUser.value, resolvedSpecs) } + } } - } + } } } @@ -301,42 +350,66 @@ constructor( private fun processExistingTile( tileSpec: TileSpec, - qsTile: QSTile, + tileOrNotInstalled: TileOrNotInstalled, userChanged: Boolean, user: Int, ): QSTile? { - return when { - !qsTile.isAvailable -> { - logger.logTileDestroyed( - tileSpec, - QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE - ) - qsTile.destroy() - null - } - // Tile is in the current list of tiles and available. - // We have a handful of different cases - qsTile !is CustomTile -> { - // The tile is not a custom tile. Make sure they are reset to the correct user - if (userChanged) { - qsTile.userSwitch(user) - logger.logTileUserChanged(tileSpec, user) + return when (tileOrNotInstalled) { + is TileOrNotInstalled.NotInstalled -> null + is TileOrNotInstalled.Tile -> { + val qsTile = tileOrNotInstalled.tile + when { + !qsTile.isAvailable -> { + logger.logTileDestroyed( + tileSpec, + QSPipelineLogger.TileDestroyedReason.EXISTING_TILE_NOT_AVAILABLE + ) + qsTile.destroy() + null + } + // Tile is in the current list of tiles and available. + // We have a handful of different cases + qsTile !is CustomTile -> { + // The tile is not a custom tile. Make sure they are reset to the correct + // user + if (userChanged) { + qsTile.userSwitch(user) + logger.logTileUserChanged(tileSpec, user) + } + qsTile + } + qsTile.user == user -> { + // The tile is a custom tile for the same user, just return it + qsTile + } + else -> { + // The tile is a custom tile and the user has changed. Destroy it + qsTile.destroy() + logger.logTileDestroyed( + tileSpec, + QSPipelineLogger.TileDestroyedReason.CUSTOM_TILE_USER_CHANGED + ) + null + } } - qsTile - } - qsTile.user == user -> { - // The tile is a custom tile for the same user, just return it - qsTile - } - else -> { - // The tile is a custom tile and the user has changed. Destroy it - qsTile.destroy() - logger.logTileDestroyed( - tileSpec, - QSPipelineLogger.TileDestroyedReason.CUSTOM_TILE_USER_CHANGED - ) - null } } } + + private sealed interface TileOrNotInstalled { + object NotInstalled : TileOrNotInstalled + + @JvmInline value class Tile(val tile: QSTile) : TileOrNotInstalled + } + + private data class UserAndTiles( + val userId: Int, + val tiles: List<TileSpec>, + ) + + private data class Data( + val oldData: UserAndTiles, + val newData: UserAndTiles, + val installedComponents: Set<ComponentName>, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt index ff7d2068bc4e..8318ec99e530 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt @@ -124,6 +124,18 @@ constructor( tileListLogBuffer.log(TILE_LIST_TAG, LogLevel.DEBUG, {}, { "Using retail tiles" }) } + fun logTilesNotInstalled(tiles: Collection<TileSpec>, user: Int) { + tileListLogBuffer.log( + TILE_LIST_TAG, + LogLevel.DEBUG, + { + str1 = tiles.toString() + int1 = user + }, + { "Tiles kept for not installed packages for user $int1: $str1" } + ) + } + /** Reasons for destroying an existing tile. */ enum class TileDestroyedReason(val readable: String) { TILE_REMOVED("Tile removed from current set"), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index 380b85cb5504..ad658458d330 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -20,7 +20,6 @@ import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_ import android.app.AlertDialog; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.net.Network; import android.net.NetworkCapabilities; @@ -58,7 +57,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; -import com.android.settingslib.Utils; import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -419,14 +417,6 @@ public class InternetDialog extends SystemUIDialog implements }); }); - TypedArray array = mContext.obtainStyledAttributes( - R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background}); - int dividerColor = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.textColorSecondary); - mMobileToggleDivider.setBackgroundColor(isNetworkConnected - ? array.getColor(0, dividerColor) : dividerColor); - array.recycle(); - mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE); mMobileToggleDivider.setVisibility( mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index e1ac0fd1fd16..2c4555a2378a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -427,6 +427,7 @@ public class RecordingService extends Service implements ScreenMediaRecorderList Log.e(TAG, "stopRecording called, but there was an error when ending" + "recording"); exception.printStackTrace(); + createErrorNotification(); } catch (Throwable throwable) { // Something unexpected happen, SystemUI will crash but let's delete // the temporary files anyway diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index 3227ef47f733..bd1b7ca7916a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -137,7 +137,12 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // Since Quick Share target recommendation does not rely on image URL, it is // queried and surfaced before image compress/export. Action intent would not be // used, because it does not contain image URL. - queryQuickShareAction(image, mParams.owner); + Notification.Action quickShare = + queryQuickShareAction(mScreenshotId, image, mParams.owner, null); + if (quickShare != null) { + mQuickShareData.quickShareAction = quickShare; + mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData); + } } // Call synchronously here since already on a background thread. @@ -176,9 +181,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { smartActionsEnabled); mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri, smartActionsEnabled); - mImageData.quickShareAction = createQuickShareAction(mContext, - mQuickShareData.quickShareAction, uri); - mImageData.subject = getSubjectString(); + mImageData.quickShareAction = createQuickShareAction( + mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image, + mParams.owner); + mImageData.subject = getSubjectString(mImageTime); mParams.mActionsReadyListener.onActionsReady(mImageData); if (DEBUG_CALLBACK) { @@ -251,7 +257,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}), new ClipData.Item(uri)); sharingIntent.setClipData(clipdata); - sharingIntent.putExtra(Intent.EXTRA_SUBJECT, getSubjectString()); + sharingIntent.putExtra(Intent.EXTRA_SUBJECT, getSubjectString(mImageTime)); sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); @@ -417,60 +423,73 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { } /** - * Populate image uri into intent of Quick Share action. + * Wrap the quickshare intent and populate the fillin intent with the URI */ @VisibleForTesting - private Notification.Action createQuickShareAction(Context context, Notification.Action action, - Uri uri) { - if (action == null) { + Notification.Action createQuickShareAction( + Notification.Action quickShare, String screenshotId, Uri uri, long imageTime, + Bitmap image, UserHandle user) { + if (quickShare == null) { return null; + } else if (quickShare.actionIntent.isImmutable()) { + Notification.Action quickShareWithUri = + queryQuickShareAction(screenshotId, image, user, uri); + if (quickShareWithUri == null + || !quickShareWithUri.title.toString().contentEquals(quickShare.title)) { + return null; + } + quickShare = quickShareWithUri; } - // Populate image URI into Quick Share chip intent - Intent sharingIntent = action.actionIntent.getIntent(); - sharingIntent.setType("image/png"); - sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); - String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); - String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); - sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); - // Include URI in ClipData also, so that grantPermission picks it up. - // We don't use setData here because some apps interpret this as "to:". - ClipData clipdata = new ClipData(new ClipDescription("content", - new String[]{"image/png"}), - new ClipData.Item(uri)); - sharingIntent.setClipData(clipdata); - sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - PendingIntent updatedPendingIntent = PendingIntent.getActivity( - context, 0, sharingIntent, - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); - - // Proxy smart actions through {@link SmartActionsReceiver} for logging smart actions. - Bundle extras = action.getExtras(); + + Intent wrappedIntent = new Intent(mContext, SmartActionsReceiver.class) + .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, quickShare.actionIntent) + .putExtra(ScreenshotController.EXTRA_ACTION_INTENT_FILLIN, + createFillInIntent(uri, imageTime)) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + Bundle extras = quickShare.getExtras(); String actionType = extras.getString( ScreenshotNotificationSmartActionsProvider.ACTION_TYPE, ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE); - Intent intent = new Intent(context, SmartActionsReceiver.class) - .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, updatedPendingIntent) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); // We only query for quick share actions when smart actions are enabled, so we can assert // that it's true here. - addIntentExtras(mScreenshotId, intent, actionType, true /* smartActionsEnabled */); - PendingIntent broadcastIntent = PendingIntent.getBroadcast(context, - mRandom.nextInt(), - intent, - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); - return new Notification.Action.Builder(action.getIcon(), action.title, - broadcastIntent).setContextual(true).addExtras(extras).build(); + addIntentExtras(screenshotId, wrappedIntent, actionType, true /* smartActionsEnabled */); + PendingIntent broadcastIntent = + PendingIntent.getBroadcast(mContext, mRandom.nextInt(), wrappedIntent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + return new Notification.Action.Builder(quickShare.getIcon(), quickShare.title, + broadcastIntent) + .setContextual(true) + .addExtras(extras) + .build(); + } + + private Intent createFillInIntent(Uri uri, long imageTime) { + Intent fillIn = new Intent(); + fillIn.setType("image/png"); + fillIn.putExtra(Intent.EXTRA_STREAM, uri); + fillIn.putExtra(Intent.EXTRA_SUBJECT, getSubjectString(imageTime)); + // Include URI in ClipData also, so that grantPermission picks it up. + // We don't use setData here because some apps interpret this as "to:". + ClipData clipData = new ClipData( + new ClipDescription("content", new String[]{"image/png"}), + new ClipData.Item(uri)); + fillIn.setClipData(clipData); + fillIn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + return fillIn; } /** * Query and surface Quick Share chip if it is available. Action intent would not be used, * because it does not contain image URL which would be populated in {@link - * #createQuickShareAction(Context, Notification.Action, Uri)} + * #createQuickShareAction(Notification.Action, String, Uri, long, Bitmap, UserHandle)} */ - private void queryQuickShareAction(Bitmap image, UserHandle user) { + + @VisibleForTesting + Notification.Action queryQuickShareAction( + String screenshotId, Bitmap image, UserHandle user, Uri uri) { CompletableFuture<List<Notification.Action>> quickShareActionsFuture = mScreenshotSmartActions.getSmartActionsFuture( - mScreenshotId, null, image, mSmartActionsProvider, + screenshotId, uri, image, mSmartActionsProvider, ScreenshotSmartActionType.QUICK_SHARE_ACTION, true /* smartActionsEnabled */, user); int timeoutMs = DeviceConfig.getInt( @@ -479,17 +498,17 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { 500); List<Notification.Action> quickShareActions = mScreenshotSmartActions.getSmartActions( - mScreenshotId, quickShareActionsFuture, timeoutMs, + screenshotId, quickShareActionsFuture, timeoutMs, mSmartActionsProvider, ScreenshotSmartActionType.QUICK_SHARE_ACTION); if (!quickShareActions.isEmpty()) { - mQuickShareData.quickShareAction = quickShareActions.get(0); - mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData); + return quickShareActions.get(0); } + return null; } - private String getSubjectString() { - String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); + private static String getSubjectString(long imageTime) { + String subjectDate = DateFormat.getDateTimeInstance().format(new Date(imageTime)); return String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 77a65b22a7f4..b59106efb769 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -246,6 +246,7 @@ public class ScreenshotController { static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled"; static final String EXTRA_OVERRIDE_TRANSITION = "android:screenshot_override_transition"; static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent"; + static final String EXTRA_ACTION_INTENT_FILLIN = "android:screenshot_action_intent_fillin"; static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification"; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java index 68b46d2b7525..ca713feafe80 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java @@ -30,7 +30,6 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -61,7 +60,6 @@ public class ScreenshotSmartActions { screenshotNotificationSmartActionsProviderProvider; } - @VisibleForTesting CompletableFuture<List<Notification.Action>> getSmartActionsFuture( String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, @@ -83,7 +81,7 @@ public class ScreenshotSmartActions { if (image.getConfig() != Bitmap.Config.HARDWARE) { if (DEBUG_ACTIONS) { Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. " - + "Returning empty list.", image.getConfig())); + + "Returning empty list.", image.getConfig())); } return CompletableFuture.completedFuture(Collections.emptyList()); } @@ -112,7 +110,6 @@ public class ScreenshotSmartActions { return smartActionsFuture; } - @VisibleForTesting List<Notification.Action> getSmartActions(String screenshotId, CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java index 45af1874e9db..9761f5931193 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java @@ -18,6 +18,7 @@ package com.android.systemui.screenshot; import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT; +import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT_FILLIN; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE; import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID; @@ -46,7 +47,9 @@ public class SmartActionsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); + PendingIntent pendingIntent = + intent.getParcelableExtra(EXTRA_ACTION_INTENT, PendingIntent.class); + Intent fillIn = intent.getParcelableExtra(EXTRA_ACTION_INTENT_FILLIN, Intent.class); String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE); if (DEBUG_ACTIONS) { Log.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent()); @@ -54,7 +57,7 @@ public class SmartActionsReceiver extends BroadcastReceiver { ActivityOptions opts = ActivityOptions.makeBasic(); try { - pendingIntent.send(context, 0, null, null, null, null, opts.toBundle()); + pendingIntent.send(context, 0, fillIn, null, null, null, opts.toBundle()); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Pending intent canceled", e); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 047fea123dc3..d2e94d6a20a5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -222,6 +222,8 @@ import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -232,7 +234,6 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Provider; -import kotlin.Unit; import kotlinx.coroutines.CoroutineDispatcher; @CentralSurfacesComponent.CentralSurfacesScope @@ -934,10 +935,11 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void onUnlockAnimationStarted( boolean playingCannedAnimation, - boolean isWakeAndUnlock, + boolean isWakeAndUnlockNotFromDream, long startDelay, long unlockAnimationDuration) { - unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlock, startDelay); + unlockAnimationStarted(playingCannedAnimation, isWakeAndUnlockNotFromDream, + startDelay); } }); mAlternateBouncerInteractor = alternateBouncerInteractor; @@ -952,7 +954,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void unlockAnimationStarted( boolean playingCannedAnimation, - boolean isWakeAndUnlock, + boolean isWakeAndUnlockNotFromDream, long unlockAnimationStartDelay) { // Disable blurs while we're unlocking so that panel expansion does not // cause blurring. This will eventually be re-enabled by the panel view on @@ -960,7 +962,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // unlock gesture, and we don't want that to cause blurring either. mDepthController.setBlursDisabledForUnlock(mTracking); - if (playingCannedAnimation && !isWakeAndUnlock) { + if (playingCannedAnimation && !isWakeAndUnlockNotFromDream) { // Hide the panel so it's not in the way or the surface behind the // keyguard, which will be appearing. If we're wake and unlocking, the // lock screen is hidden instantly so should not be flung away. @@ -1651,10 +1653,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)); mKeyguardNotificationBottomPadding = bottomPadding; - float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding() - // getMinStackScrollerPadding is from the top of the screen, - // but we need it from the top of the NSSL. - - mNotificationStackScrollLayoutController.getTop(); + float staticTopPadding = mClockPositionAlgorithm.getLockscreenNotifPadding( + mNotificationStackScrollLayoutController.getTop()); + mKeyguardNotificationTopPadding = staticTopPadding; // To debug the available space, enable debug lines in this class. If you change how the @@ -1668,8 +1669,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump Log.i(TAG, "\n"); Log.i(TAG, "staticTopPadding[" + staticTopPadding + "] = Clock.padding[" - + mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding() - + "] - NSSLC.top[" + mNotificationStackScrollLayoutController.getTop() + + mClockPositionAlgorithm.getLockscreenNotifPadding( + mNotificationStackScrollLayoutController.getTop()) + "]" ); Log.i(TAG, "bottomPadding[" + bottomPadding @@ -2010,6 +2011,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } updateExpansionAndVisibility(); mNotificationStackScrollLayoutController.setPanelFlinging(false); + // expandImmediate should be always reset at the end of animation + mQsController.setExpandImmediate(false); } private boolean isInContentBounds(float x, float y) { @@ -3450,6 +3453,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @VisibleForTesting void notifyExpandingStarted() { if (!mExpanding) { + DejankUtils.notifyRendererOfExpensiveFrame(mView, "notifyExpandingStarted"); mExpanding = true; mIsExpandingOrCollapsing = true; mQsController.onExpandingStarted(mQsController.getFullyExpanded()); diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index c42c2f4fa15a..6480164cdaf5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -63,6 +63,7 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.policy.SystemBarUtils; import com.android.keyguard.FaceAuthApiRequestReason; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.classifier.Classifier; @@ -958,6 +959,7 @@ public class QuickSettingsController implements Dumpable { // TODO (b/265193930): remove dependency on NPVC mPanelViewControllerLazy.get().cancelHeightAnimator(); // end + DejankUtils.notifyRendererOfExpensiveFrame(mPanelView, "onExpansionStarted"); // Reset scroll position and apply that position to the expanded height. float height = mExpansionHeight; diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index f080d3dfab1d..3af75cef3d4c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -34,6 +34,7 @@ import android.view.WindowInsets import android.widget.TextView import androidx.annotation.VisibleForTesting import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.core.view.doOnLayout import com.android.app.animation.Interpolators import com.android.settingslib.Utils import com.android.systemui.Dumpable @@ -220,6 +221,7 @@ constructor( override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK) override fun dispatchDemoCommand(command: String, args: Bundle) = clock.dispatchDemoCommand(command, args) + override fun onDemoModeStarted() = clock.onDemoModeStarted() override fun onDemoModeFinished() = clock.onDemoModeFinished() } @@ -259,6 +261,7 @@ constructor( resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height) lastInsets?.let { updateConstraintsForInsets(header, it) } updateResources() + updateCarrierGroupPadding() } } @@ -291,6 +294,7 @@ constructor( privacyIconsController.chipVisibilityListener = chipVisibilityListener updateVisibility() updateTransition() + updateCarrierGroupPadding() header.setOnApplyWindowInsetsListener(insetListener) @@ -298,8 +302,6 @@ constructor( val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f v.pivotX = newPivot v.pivotY = v.height.toFloat() / 2 - - mShadeCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0) } clock.setOnClickListener { launchClockActivity() } @@ -359,6 +361,14 @@ constructor( .load(context, resources.getXml(R.xml.large_screen_shade_header)) } + private fun updateCarrierGroupPadding() { + clock.doOnLayout { + val maxClockWidth = + (clock.width * resources.getFloat(R.dimen.qqs_expand_clock_scale)).toInt() + mShadeCarrierGroup.setPaddingRelative(maxClockWidth, 0, 0, 0) + } + } + private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) { val cutout = insets.displayCutout.also { this.cutout = it } diff --git a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt index 641131e4dcc1..03be88fc31d9 100644 --- a/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt +++ b/packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -34,6 +34,11 @@ abstract class SmartspaceModule { const val DREAM_SMARTSPACE_DATA_PLUGIN = "dreams_smartspace_data_plugin" /** + * The BcSmartspaceDataPlugin for the standalone weather on dream. + */ + const val DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN = "dream_weather_smartspace_data_plugin" + + /** * The dream smartspace target filter. */ const val DREAM_SMARTSPACE_TARGET_FILTER = "dream_smartspace_target_filter" @@ -62,6 +67,10 @@ abstract class SmartspaceModule { @Named(DREAM_SMARTSPACE_DATA_PLUGIN) abstract fun optionalDreamsBcSmartspaceDataPlugin(): BcSmartspaceDataPlugin? + @BindsOptionalOf + @Named(DREAM_WEATHER_SMARTSPACE_DATA_PLUGIN) + abstract fun optionalDreamWeatherSmartspaceDataPlugin(): BcSmartspaceDataPlugin? + @Binds @Named(DREAM_SMARTSPACE_PRECONDITION) abstract fun bindSmartspacePrecondition( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index b6970aef6cad..39d213d46c9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -83,7 +83,8 @@ open class BlurUtils @Inject constructor( return } if (lastAppliedBlur == 0 && radius != 0) { - Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, EARLY_WAKEUP_SLICE_NAME, 0) + Trace.asyncTraceForTrackBegin( + TRACE_TAG_APP, TRACK_NAME, "eEarlyWakeup (prepareBlur)", 0) earlyWakeupEnabled = true createTransaction().use { it.setEarlyWakeupStart() @@ -110,7 +111,7 @@ open class BlurUtils @Inject constructor( Trace.asyncTraceForTrackBegin( TRACE_TAG_APP, TRACK_NAME, - EARLY_WAKEUP_SLICE_NAME, + "eEarlyWakeup (applyBlur)", 0 ) it.setEarlyWakeupStart() @@ -159,6 +160,5 @@ open class BlurUtils @Inject constructor( companion object { const val TRACK_NAME = "BlurUtils" - const val EARLY_WAKEUP_SLICE_NAME = "eEarlyWakeup" } } 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 0a18f2d89d87..56ea703668d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt @@ -188,7 +188,9 @@ constructor( if (animationState.value == ANIMATING_OUT) { coroutineScope.launch { withTimeout(DISAPPEAR_ANIMATION_DURATION) { - animationState.first { it == SHOWING_PERSISTENT_DOT || it == ANIMATION_QUEUED } + animationState.first { + it == SHOWING_PERSISTENT_DOT || it == IDLE || it == ANIMATION_QUEUED + } notifyHidePersistentDot() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index 23b5241b79ef..314566eaf8d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -633,12 +633,17 @@ class HeadsUpCoordinator @Inject constructor( mFSIUpdateCandidates.removeAll(toRemoveForFSI) } - /** When an action is pressed on a notification, end HeadsUp lifetime extension. */ + /** + * When an action is pressed on a notification, make sure we don't lifetime-extend it in the + * future by informing the HeadsUpManager, and make sure we don't keep lifetime-extending it if + * we already are. + * + * @see HeadsUpManager.setUserActionMayIndirectlyRemove + * @see HeadsUpManager.canRemoveImmediately + */ private val mActionPressListener = Consumer<NotificationEntry> { entry -> - if (mNotifsExtendingLifetime.contains(entry)) { - val removeInMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key) - mExecutor.executeDelayed({ endNotifLifetimeExtensionIfExtended(entry) }, removeInMillis) - } + mHeadsUpManager.setUserActionMayIndirectlyRemove(entry) + mExecutor.execute { endNotifLifetimeExtensionIfExtended(entry) } } private val mLifetimeExtender = object : NotifLifetimeExtender { 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 bfb6fb0fcdc7..9f397fe9ac0c 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 @@ -556,6 +556,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public void onNotificationUpdated() { + if (mIsSummaryWithChildren) { + Trace.beginSection("ExpNotRow#onNotifUpdated (summary)"); + } else { + Trace.beginSection("ExpNotRow#onNotifUpdated (leaf)"); + } for (NotificationContentView l : mLayouts) { l.onNotificationUpdated(mEntry); } @@ -591,6 +596,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mUpdateSelfBackgroundOnUpdate = false; updateBackgroundColorsOfSelf(); } + Trace.endSection(); } private void updateBackgroundColorsOfSelf() { @@ -2588,6 +2594,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mIsSummaryWithChildren = mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0; if (mIsSummaryWithChildren) { + Trace.beginSection("ExpNotRow#onChildCountChanged (summary)"); NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper(); if (wrapper == null || wrapper.getNotificationHeader() == null) { mChildrenContainer.recreateNotificationHeader(mExpandClickListener, @@ -2599,6 +2606,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateChildrenAppearance(); updateChildrenVisibility(); applyChildrenRoundness(); + if (mIsSummaryWithChildren) { + Trace.endSection(); + } } protected void expandNotification() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java index 77fd05186090..3e10f2ad52e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.Notification; import android.content.Context; import android.content.res.Resources; +import android.os.Trace; import android.service.notification.StatusBarNotification; import android.util.TypedValue; import android.view.LayoutInflater; @@ -57,6 +58,7 @@ public class HybridGroupManager { } private HybridNotificationView inflateHybridView(View contentView, ViewGroup parent) { + Trace.beginSection("HybridGroupManager#inflateHybridView"); LayoutInflater inflater = LayoutInflater.from(mContext); int layout = contentView instanceof ConversationLayout ? R.layout.hybrid_conversation_notification @@ -64,6 +66,7 @@ public class HybridGroupManager { HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(layout, parent, false); parent.addView(hybrid); + Trace.endSection(); return hybrid; } @@ -90,12 +93,18 @@ public class HybridGroupManager { public HybridNotificationView bindFromNotification(HybridNotificationView reusableView, View contentView, StatusBarNotification notification, ViewGroup parent) { + boolean isNewView = false; if (reusableView == null) { + Trace.beginSection("HybridGroupManager#bindFromNotification"); reusableView = inflateHybridView(contentView, parent); + isNewView = true; } CharSequence titleText = resolveTitle(notification.getNotification()); CharSequence contentText = resolveText(notification.getNotification()); reusableView.bind(titleText, contentText, contentView); + if (isNewView) { + Trace.endSection(); + } return reusableView; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 451d837b63a0..124df8c3b815 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -26,6 +26,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.RemoteException; +import android.os.Trace; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.AttributeSet; @@ -1193,6 +1194,7 @@ public class NotificationContentView extends FrameLayout implements Notification private void updateSingleLineView() { if (mIsChildInGroup) { + Trace.beginSection("NotifContentView#updateSingleLineView"); boolean isNewView = mSingleLineView == null; mSingleLineView = mHybridGroupManager.bindFromNotification( mSingleLineView, mContractedChild, mNotificationEntry.getSbn(), this); @@ -1200,6 +1202,7 @@ public class NotificationContentView extends FrameLayout implements Notification updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView); } + Trace.endSection(); } else if (mSingleLineView != null) { removeView(mSingleLineView); mSingleLineView = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 0c4b0927a906..7dbca4276fcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -552,6 +552,9 @@ public class NotificationGutsManager implements NotifGutsViewManager { Runnable r = () -> mMainHandler.post( () -> openGutsInternal(view, x, y, menuItem)); + // If the bouncer shows, it will block the TOUCH_UP event from reaching the notif, + // so explicitly mark it as unpressed here to reset the touch animation. + view.setPressed(false); mActivityStarter.executeRunnableDismissingKeyguard( r, null /* cancelAction */, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index d18757d3453f..f8e374de11e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -379,6 +379,7 @@ public class NotificationChildrenContainer extends ViewGroup } public void recreateNotificationHeader(OnClickListener listener, boolean isConversation) { + Trace.beginSection("NotifChildCont#recreateHeader"); mHeaderClickListener = listener; mIsConversation = isConversation; StatusBarNotification notification = mContainingNotification.getEntry().getSbn(); @@ -406,6 +407,7 @@ public class NotificationChildrenContainer extends ViewGroup recreateLowPriorityHeader(builder, isConversation); updateHeaderVisibility(false /* animate */); updateChildrenAppearance(); + Trace.endSection(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index b81cb2be1c4d..df6fe3d5e434 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -4011,7 +4011,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mCentralSurfaces.resetUserExpandedStates(); clearTemporaryViews(); clearUserLockedViews(); - cancelActiveSwipe(); + resetAllSwipeState(); } } @@ -4077,8 +4077,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mGroupExpansionManager.collapseGroups(); mExpandHelper.cancelImmediately(); if (!mIsExpansionChanging) { - cancelActiveSwipe(); + resetAllSwipeState(); } + finalizeClearAllAnimation(); } updateNotificationAnimationStates(); updateChronometers(); @@ -4174,20 +4175,30 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable runAnimationFinishedRunnables(); clearTransient(); clearHeadsUpDisappearRunning(); + finalizeClearAllAnimation(); + } + private void finalizeClearAllAnimation() { if (mAmbientState.isClearAllInProgress()) { setClearAllInProgress(false); if (mShadeNeedsToClose) { mShadeNeedsToClose = false; - postDelayed( - () -> { - mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE); - }, - DELAY_BEFORE_SHADE_CLOSE /* delayMillis */); + if (mIsExpanded) { + collapseShadeDelayed(); + } } } } + private void collapseShadeDelayed() { + postDelayed( + () -> { + mShadeController.animateCollapseShade( + CommandQueue.FLAG_EXCLUDE_NONE); + }, + DELAY_BEFORE_SHADE_CLOSE /* delayMillis */); + } + private void clearHeadsUpDisappearRunning() { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); @@ -4395,6 +4406,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable boolean nowHiddenAtAll = mAmbientState.isHiddenAtAll(); if (nowFullyHidden != wasFullyHidden) { updateVisibility(); + resetAllSwipeState(); } if (!wasHiddenAtAll && nowHiddenAtAll) { resetExposedMenuView(true /* animate */, true /* animate */); @@ -4535,6 +4547,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications); } + @VisibleForTesting public void setClearAllInProgress(boolean clearAllInProgress) { mClearAllInProgress = clearAllInProgress; mAmbientState.setClearAllInProgress(clearAllInProgress); @@ -4688,6 +4701,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) { mController.updateShowEmptyShadeView(); updateFooter(); + mController.updateImportantForAccessibility(); } updateSpeedBumpIndex(); @@ -4699,6 +4713,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) { mController.updateShowEmptyShadeView(); updateFooter(); + mController.updateImportantForAccessibility(); } updateSpeedBumpIndex(); @@ -4711,6 +4726,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) { mController.updateShowEmptyShadeView(); updateFooter(); + mController.updateImportantForAccessibility(); } updateSpeedBumpIndex(); @@ -5151,7 +5167,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mHeadsUpAppearanceController = headsUpAppearanceController; } - private boolean isVisible(View child) { + @VisibleForTesting + public boolean isVisible(View child) { boolean hasClipBounds = child.getClipBounds(mTmpRect); return child.getVisibility() == View.VISIBLE && (!hasClipBounds || mTmpRect.height() > 0); @@ -5849,9 +5866,14 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } } - private void cancelActiveSwipe() { - mSwipeHelper.resetSwipeState(); + private void resetAllSwipeState() { + Trace.beginSection("NSSL.resetAllSwipeState()"); + mSwipeHelper.resetTouchState(); + for (int i = 0; i < getChildCount(); i++) { + mSwipeHelper.forceResetSwipeState(getChildAt(i)); + } updateContinuousShadowDrawing(); + Trace.endSection(); } void updateContinuousShadowDrawing() { 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 7b046d6c9256..9272c376d4fe 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 @@ -327,6 +327,7 @@ public class NotificationStackScrollLayoutController { mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(), mLockscreenUserManager.isAnyProfilePublicMode()); mView.onStatePostChange(mStatusBarStateController.fromShadeLocked()); + updateImportantForAccessibility(); } }; @@ -1205,6 +1206,7 @@ public class NotificationStackScrollLayoutController { if (mView.getVisibility() == View.VISIBLE) { // Synchronize EmptyShadeView visibility with the parent container. updateShowEmptyShadeView(); + updateImportantForAccessibility(); } } @@ -1232,6 +1234,22 @@ public class NotificationStackScrollLayoutController { } /** + * Update the importantForAccessibility of NotificationStackScrollLayout. + * <p> + * We want the NSSL to be unimportant for accessibility when there's no + * notifications in it while the device is on lock screen, to avoid unlablel NSSL view. + * Otherwise, we want it to be important for accessibility to enable accessibility + * auto-scrolling in NSSL. + */ + public void updateImportantForAccessibility() { + if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) { + mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } else { + mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + } + + /** * @return true if {@link StatusBarStateController} is in transition to the KEYGUARD * and false otherwise. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index b956207190b7..7e327e66982c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -34,6 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.InteractionJankMonitor; import com.android.systemui.SwipeHelper; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -545,14 +546,17 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc private final FeatureFlags mFeatureFlags; private NotificationCallback mNotificationCallback; private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener; + private DumpManager mDumpManager; private NotificationRoundnessManager mNotificationRoundnessManager; @Inject Builder(@Main Resources resources, ViewConfiguration viewConfiguration, + DumpManager dumpManager, FalsingManager falsingManager, FeatureFlags featureFlags, NotificationRoundnessManager notificationRoundnessManager) { mResources = resources; mViewConfiguration = viewConfiguration; + mDumpManager = dumpManager; mFalsingManager = falsingManager; mFeatureFlags = featureFlags; mNotificationRoundnessManager = notificationRoundnessManager; @@ -570,9 +574,12 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc } NotificationSwipeHelper build() { - return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager, + NotificationSwipeHelper swipeHelper = new NotificationSwipeHelper( + mResources, mViewConfiguration, mFalsingManager, mFeatureFlags, mNotificationCallback, mOnMenuEventListener, mNotificationRoundnessManager); + mDumpManager.registerDumpable(swipeHelper); + return swipeHelper; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 6f1c378f429d..efb7926b8b8b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -315,7 +315,8 @@ public class StackScrollAlgorithm { float newNotificationEnd = newYTranslation + newHeight; boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && child.isPinned(); if (mClipNotificationScrollToTop - && ((isHeadsUp && !firstHeadsUp) || child.isHeadsUpAnimatingAway()) + && !firstHeadsUp + && (isHeadsUp || child.isHeadsUpAnimatingAway()) && newNotificationEnd > firstHeadsUpEnd && !ambientState.isShadeExpanded()) { // The bottom of this view is peeking out from under the previous view. @@ -619,13 +620,12 @@ public class StackScrollAlgorithm { updateViewWithShelf(view, viewState, shelfStart); } } - // Avoid pulsing notification flicker during AOD to LS - // A pulsing notification is already expanded, no need to expand it again with animation - if (ambientState.isPulsingRow(view)) { - expansionFraction = 1.0f; + viewState.height = getMaxAllowedChildHeight(view); + if (!view.isPinned() && !view.isHeadsUpAnimatingAway() + && !ambientState.isPulsingRow(view)) { + // The expansion fraction should not affect HUNs or pulsing notifications. + viewState.height *= expansionFraction; } - // Clip height of view right before shelf. - viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction); } algorithmState.mCurrentYPosition += @@ -785,6 +785,8 @@ public class StackScrollAlgorithm { } } if (row.isPinned()) { + // Make sure row yTranslation is at maximum the HUN yTranslation, + // which accounts for AmbientState.stackTopMargin in split-shade. childState.setYTranslation( Math.max(childState.getYTranslation(), headsUpTranslation)); childState.height = Math.max(row.getIntrinsicHeight(), childState.height); @@ -809,7 +811,11 @@ public class StackScrollAlgorithm { } } if (row.isHeadsUpAnimatingAway()) { - childState.setYTranslation(Math.max(childState.getYTranslation(), mHeadsUpInset)); + // Make sure row yTranslation is at maximum the HUN yTranslation, + // which accounts for AmbientState.stackTopMargin in split-shade. + childState.setYTranslation( + Math.max(childState.getYTranslation(), headsUpTranslation)); + // keep it visible for the animation childState.hidden = false; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 0402d4f88205..1cd0f081a744 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2836,7 +2836,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } private void updateDozingState() { - Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0); + if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) { + Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "Dozing", 0); + Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "Dozing", String.valueOf(mDozing), + 0); + } Trace.beginSection("CentralSurfaces#updateDozingState"); boolean keyguardVisible = mKeyguardStateController.isVisible(); @@ -3190,6 +3194,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void onStartedWakingUp() { + // Between onStartedWakingUp() and onFinishedWakingUp(), the system is changing the + // display power mode. To avoid jank, animations should NOT run during these power + // mode transitions, which means that whenever possible, animations should + // start running during the onFinishedWakingUp() callback instead of this callback. String tag = "CentralSurfaces#onStartedWakingUp"; DejankUtils.startDetectingBlockingIpcs(tag); mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> { @@ -3234,26 +3242,41 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { updateVisibleToUser(); updateIsKeyguard(); - mDozeServiceHost.stopDozing(); - // This is intentionally below the stopDozing call above, since it avoids that we're - // unnecessarily animating the wakeUp transition. Animations should only be enabled - // once we fully woke up. - updateRevealEffect(true /* wakingUp */); - updateNotificationPanelTouchState(); - mStatusBarTouchableRegionManager.updateTouchableRegion(); - - // If we are waking up during the screen off animation, we should undo making the - // expanded visible (we did that so the LightRevealScrim would be visible). - if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) { - mShadeController.makeExpandedInvisible(); + if (!mFeatureFlags.isEnabled(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD)) { + startLockscreenTransitionFromAod(); } - }); DejankUtils.stopDetectingBlockingIpcs(tag); } + /** + * Private helper for starting the LOCKSCREEN_TRANSITION_FROM_AOD animation - only necessary + * so we can start it from either onFinishedWakingUp() or onFinishedWakingUp() depending + * on a flag value. + */ + private void startLockscreenTransitionFromAod() { + // stopDozing() starts the LOCKSCREEN_TRANSITION_FROM_AOD animation. + mDozeServiceHost.stopDozing(); + // This is intentionally below the stopDozing call above, since it avoids that we're + // unnecessarily animating the wakeUp transition. Animations should only be enabled + // once we fully woke up. + updateRevealEffect(true /* wakingUp */); + updateNotificationPanelTouchState(); + mStatusBarTouchableRegionManager.updateTouchableRegion(); + + // If we are waking up during the screen off animation, we should undo making the + // expanded visible (we did that so the LightRevealScrim would be visible). + if (mScreenOffAnimationController.shouldHideLightRevealScrimOnWakeUp()) { + mShadeController.makeExpandedInvisible(); + } + } + @Override public void onFinishedWakingUp() { + if (mFeatureFlags.isEnabled(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD)) { + mNotificationShadeWindowController.batchApplyWindowLayoutParams( + this::startLockscreenTransitionFromAod); + } mWakeUpCoordinator.setFullyAwake(true); mWakeUpCoordinator.setWakingUp(false, false); if (mKeyguardStateController.isOccluded() @@ -3677,7 +3700,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private void onShadeVisibilityChanged(boolean visible) { if (mVisible != visible) { mVisible = visible; - if (!visible) { + if (visible) { + DejankUtils.notifyRendererOfExpensiveFrame( + mNotificationShadeWindowView, "onShadeVisibilityChanged"); + } else { mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 561bd91b964f..fc3c85a0807b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -229,12 +229,18 @@ public class KeyguardClockPositionAlgorithm { } } - public float getLockscreenMinStackScrollerPadding() { + /** + * @param nsslTop NotificationStackScrollLayout top, which is below top of the srceen. + * @return Distance from nsslTop to top of the first view in the lockscreen shade. + */ + public float getLockscreenNotifPadding(float nsslTop) { if (mBypassEnabled) { - return mUnlockedStackScrollerPadding; + return mUnlockedStackScrollerPadding - nsslTop; } else if (mIsSplitShade) { - return mSplitShadeTargetTopMargin + mUserSwitchHeight; + return mSplitShadeTargetTopMargin + mUserSwitchHeight - nsslTop; } else { + // Non-bypass portrait shade already uses values from nsslTop + // so we don't need to subtract it here. return mMinTopMargin + mKeyguardStatusHeight; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index b36ba3845fe9..d42e30c9b1b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -110,6 +110,9 @@ interface MobileIconInteractor { /** See [MobileIconsInteractor.isForceHidden]. */ val isForceHidden: Flow<Boolean> + + /** True when in carrier network change mode */ + val carrierNetworkChangeActive: StateFlow<Boolean> } /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */ @@ -135,6 +138,9 @@ class MobileIconInteractorImpl( override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled + override val carrierNetworkChangeActive: StateFlow<Boolean> = + connectionRepository.carrierNetworkChangeActive + // True if there exists _any_ icon override for this carrierId. Note that overrides can include // any or none of the icon groups defined in MobileMappings, so we still need to check on a // per-network-type basis whether or not the given icon group is overridden diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index 5b7d45b55c5c..a2a247a279fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -109,12 +109,7 @@ object MobileIconBinder { viewModel.subscriptionId, icon, ) - mobileDrawable.level = - SignalDrawable.getState( - icon.level, - icon.numberOfLevels, - icon.showExclamationMark, - ) + mobileDrawable.level = icon.toSignalDrawableState() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt index be2e41a2a581..6de3f85027ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.model +import com.android.settingslib.graph.SignalDrawable import com.android.systemui.log.table.Diffable import com.android.systemui.log.table.TableRowLogger @@ -24,6 +25,7 @@ data class SignalIconModel( val level: Int, val numberOfLevels: Int, val showExclamationMark: Boolean, + val carrierNetworkChange: Boolean, ) : Diffable<SignalIconModel> { // TODO(b/267767715): Can we implement [logDiffs] and [logFull] generically for data classes? override fun logDiffs(prevVal: SignalIconModel, row: TableRowLogger) { @@ -36,17 +38,30 @@ data class SignalIconModel( if (prevVal.showExclamationMark != showExclamationMark) { row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark) } + if (prevVal.carrierNetworkChange != carrierNetworkChange) { + row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChange) + } } override fun logFull(row: TableRowLogger) { row.logChange(COL_LEVEL, level) row.logChange(COL_NUM_LEVELS, numberOfLevels) row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark) + row.logChange(COL_CARRIER_NETWORK_CHANGE, carrierNetworkChange) } + /** Convert this model to an [Int] consumable by [SignalDrawable]. */ + fun toSignalDrawableState(): Int = + if (carrierNetworkChange) { + SignalDrawable.getCarrierChangeState(numberOfLevels) + } else { + SignalDrawable.getState(level, numberOfLevels, showExclamationMark) + } + companion object { private const val COL_LEVEL = "level" private const val COL_NUM_LEVELS = "numLevels" private const val COL_SHOW_EXCLAMATION = "showExclamation" + private const val COL_CARRIER_NETWORK_CHANGE = "carrierNetworkChange" } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index 54730ed82564..35f4f9aa4622 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -122,13 +122,20 @@ constructor( level = shownLevel.value, numberOfLevels = iconInteractor.numberOfLevels.value, showExclamationMark = showExclamationMark.value, + carrierNetworkChange = iconInteractor.carrierNetworkChangeActive.value, ) combine( shownLevel, iconInteractor.numberOfLevels, showExclamationMark, - ) { shownLevel, numberOfLevels, showExclamationMark -> - SignalIconModel(shownLevel, numberOfLevels, showExclamationMark) + iconInteractor.carrierNetworkChangeActive, + ) { shownLevel, numberOfLevels, showExclamationMark, carrierNetworkChange -> + SignalIconModel( + shownLevel, + numberOfLevels, + showExclamationMark, + carrierNetworkChange, + ) } .distinctUntilChanged() .logDiffsForTable( @@ -152,8 +159,10 @@ constructor( iconInteractor.isDataEnabled, iconInteractor.alwaysShowDataRatIcon, iconInteractor.mobileIsDefault, - ) { dataConnected, dataEnabled, alwaysShow, mobileIsDefault -> - alwaysShow || (dataEnabled && dataConnected && mobileIsDefault) + iconInteractor.carrierNetworkChangeActive, + ) { dataConnected, dataEnabled, alwaysShow, mobileIsDefault, carrierNetworkChange -> + alwaysShow || + (!carrierNetworkChange && (dataEnabled && dataConnected && mobileIsDefault)) } .distinctUntilChanged() .logDiffsForTable( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index ed8050a031d8..92a78541d744 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -393,6 +393,31 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { } } + /** + * Notes that the user took an action on an entry that might indirectly cause the system or the + * app to remove the notification. + * + * @param entry the entry that might be indirectly removed by the user's action + * + * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener + * @see #canRemoveImmediately(String) + */ + public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) { + HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey()); + if (headsUpEntry != null) { + headsUpEntry.userActionMayIndirectlyRemove = true; + } + } + + @Override + public boolean canRemoveImmediately(@NonNull String key) { + HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); + if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) { + return true; + } + return super.canRemoveImmediately(key); + } + @NonNull @Override protected HeadsUpEntry createAlertEntry() { @@ -421,6 +446,8 @@ public abstract class HeadsUpManager extends AlertingNotificationManager { */ protected class HeadsUpEntry extends AlertEntry { public boolean remoteInputActive; + public boolean userActionMayIndirectlyRemove; + protected boolean expanded; protected boolean wasUnpinned; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index d585163aa223..02b7e9176cf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -21,7 +21,6 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD; import android.app.ActivityManager; -import android.app.Notification; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ColorStateList; @@ -47,7 +46,6 @@ import android.view.OnReceiveContentListener; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; -import android.view.ViewGroupOverlay; import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; @@ -58,6 +56,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; @@ -436,25 +435,23 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (animate && parent != null && mIsFocusAnimationFlagActive) { ViewGroup grandParent = (ViewGroup) parent.getParent(); - ViewGroupOverlay overlay = parent.getOverlay(); View actionsContainer = getActionsContainerLayout(); int actionsContainerHeight = actionsContainer != null ? actionsContainer.getHeight() : 0; - // After adding this RemoteInputView to the overlay of the parent (and thus removing - // it from the parent itself), the parent will shrink in height. This causes the - // overlay to be moved. To correct the position of the overlay we need to offset it. - int overlayOffsetY = actionsContainerHeight - getHeight(); - overlay.add(this); + // When defocusing, the notification needs to shrink. Therefore, we need to free + // up the space that was needed for the RemoteInputView. This is done by setting + // a negative top margin of the height difference of the RemoteInputView and its + // sibling (the actions_container_layout containing the Reply button etc.) + final int heightToShrink = actionsContainerHeight - getHeight(); + setTopMargin(heightToShrink); if (grandParent != null) grandParent.setClipChildren(false); - Animator animator = getDefocusAnimator(actionsContainer, overlayOffsetY); - View self = this; + final Animator animator = getDefocusAnimator(actionsContainer); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - overlay.remove(self); - parent.addView(self); + setTopMargin(0); if (grandParent != null) grandParent.setClipChildren(true); setVisibility(GONE); if (mWrapper != null) { @@ -499,6 +496,13 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } + private void setTopMargin(int topMargin) { + if (!(getLayoutParams() instanceof FrameLayout.LayoutParams)) return; + final FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) getLayoutParams(); + layoutParams.topMargin = topMargin; + setLayoutParams(layoutParams); + } + @VisibleForTesting protected void setViewRootImpl(ViewRootImpl viewRoot) { mTestableViewRootImpl = viewRoot; @@ -674,12 +678,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mProgressBar.setVisibility(INVISIBLE); mResetting = true; mSending = false; + mController.removeSpinning(mEntry.getKey(), mToken); onDefocus(true /* animate */, false /* logClose */, () -> { mEntry.remoteInputTextWhenReset = SpannedString.valueOf(mEditText.getText()); mEditText.getText().clear(); mEditText.setEnabled(isAggregatedVisible()); mSendButton.setVisibility(VISIBLE); - mController.removeSpinning(mEntry.getKey(), mToken); updateSendButton(); setAttachment(null); mResetting = false; @@ -874,7 +878,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ValueAnimator scaleAnimator = ValueAnimator.ofFloat(FOCUS_ANIMATION_MIN_SCALE, 1f); scaleAnimator.addUpdateListener(valueAnimator -> { - setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue(), 0); + setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue()); }); scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION); scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN); @@ -901,9 +905,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene * Creates an animator for the defocus animation. * * @param fadeInView View that will be faded in during the defocus animation. - * @param offsetY The RemoteInputView will be offset by offsetY during the animation */ - private Animator getDefocusAnimator(@Nullable View fadeInView, int offsetY) { + private Animator getDefocusAnimator(@Nullable View fadeInView) { final AnimatorSet animatorSet = new AnimatorSet(); final Animator alphaAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 1f, 0f); @@ -913,14 +916,14 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene ValueAnimator scaleAnimator = ValueAnimator.ofFloat(1f, FOCUS_ANIMATION_MIN_SCALE); scaleAnimator.addUpdateListener(valueAnimator -> { - setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue(), offsetY); + setFocusAnimationScaleY((float) scaleAnimator.getAnimatedValue()); }); scaleAnimator.setDuration(FOCUS_ANIMATION_TOTAL_DURATION); scaleAnimator.setInterpolator(InterpolatorsAndroidX.FAST_OUT_SLOW_IN); scaleAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation, boolean isReverse) { - setFocusAnimationScaleY(1f /* scaleY */, 0 /* verticalOffset */); + setFocusAnimationScaleY(1f /* scaleY */); } }); @@ -942,9 +945,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene * Sets affected view properties for a vertical scale animation * * @param scaleY desired vertical view scale - * @param verticalOffset vertical offset to apply to the RemoteInputView during the animation */ - private void setFocusAnimationScaleY(float scaleY, int verticalOffset) { + private void setFocusAnimationScaleY(float scaleY) { int verticalBoundOffset = (int) ((1f - scaleY) * 0.5f * mContentView.getHeight()); Rect contentBackgroundBounds = new Rect(0, verticalBoundOffset, mContentView.getWidth(), mContentView.getHeight() - verticalBoundOffset); @@ -955,7 +957,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } else { mContentBackgroundBounds = contentBackgroundBounds; } - setTranslationY(verticalBoundOffset + verticalOffset); + setTranslationY(verticalBoundOffset); } /** Handler for button click on send action in IME. */ diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt index 8214822f0335..1e73cb3b9b24 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldHapticsPlayer.kt @@ -1,6 +1,7 @@ package com.android.systemui.unfold import android.os.SystemProperties +import android.os.VibrationAttributes import android.os.VibrationEffect import android.os.Vibrator import com.android.systemui.dagger.qualifiers.Main @@ -22,6 +23,8 @@ constructor( ) : TransitionProgressListener { private var isFirstAnimationAfterUnfold = false + private val touchVibrationAttributes = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK) init { if (vibrator != null) { @@ -71,7 +74,7 @@ constructor( } private fun playHaptics() { - vibrator?.vibrate(effect) + vibrator?.vibrate(effect, touchVibrationAttributes) } private val hapticsScale: Float diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index 2709da38a7d8..992b0221068c 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -18,16 +18,19 @@ package com.android.systemui.unfold import android.content.Context import android.hardware.devicestate.DeviceStateManager +import android.os.SystemProperties import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.LifecycleScreenStatusProvider import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.system.SystemUnfoldSharedModule +import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider +import com.android.systemui.unfold.util.UnfoldOnlyProgressProvider import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix import com.android.systemui.util.time.SystemClockImpl import com.android.wm.shell.unfold.ShellUnfoldProgressProvider @@ -37,6 +40,7 @@ import dagger.Provides import java.util.Optional import java.util.concurrent.Executor import javax.inject.Named +import javax.inject.Provider import javax.inject.Singleton @Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class]) @@ -91,6 +95,18 @@ class UnfoldTransitionModule { } @Provides + @Singleton + @Named(UNFOLD_ONLY_PROVIDER) + fun provideUnfoldOnlyProvider( + foldProvider: FoldProvider, + @Main executor: Executor, + sourceProvider: Optional<UnfoldTransitionProgressProvider> + ): Optional<UnfoldTransitionProgressProvider> = + sourceProvider.map { provider -> + UnfoldOnlyProgressProvider(foldProvider, executor, provider) + } + + @Provides @Named(UNFOLD_STATUS_BAR) @Singleton fun provideStatusBarScopedTransitionProvider( @@ -102,16 +118,35 @@ class UnfoldTransitionModule { @Singleton fun provideShellProgressProvider( config: UnfoldTransitionConfig, - provider: Optional<UnfoldTransitionProgressProvider> - ): ShellUnfoldProgressProvider = - if (config.isEnabled && provider.isPresent) { - UnfoldProgressProvider(provider.get()) - } else { - ShellUnfoldProgressProvider.NO_PROVIDER - } + provider: Provider<Optional<UnfoldTransitionProgressProvider>>, + @Named(UNFOLD_ONLY_PROVIDER) + unfoldOnlyProvider: Provider<Optional<UnfoldTransitionProgressProvider>> + ): ShellUnfoldProgressProvider { + val resultingProvider = + if (config.isEnabled) { + // Return unfold only provider to the shell if we don't want to animate tasks during + // folding. Shell provider listeners are responsible for animating task bounds. + if (ENABLE_FOLD_TASK_ANIMATIONS) { + provider + } else { + unfoldOnlyProvider + } + } else { + null + } + + return resultingProvider?.get()?.orElse(null)?.let(::UnfoldProgressProvider) + ?: ShellUnfoldProgressProvider.NO_PROVIDER + } @Provides fun screenStatusProvider(impl: LifecycleScreenStatusProvider): ScreenStatusProvider = impl } const val UNFOLD_STATUS_BAR = "unfold_status_bar" +const val UNFOLD_ONLY_PROVIDER = "unfold_only_provider" + +// TODO: b/265764985 - tracking bug to clean-up the flag +// FeatureFlags are not accessible here because it's a global submodule (see GlobalModule.java) +private val ENABLE_FOLD_TASK_ANIMATIONS = + SystemProperties.getBoolean("persist.unfold.enable_fold_tasks_animation", false) diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt index 7236e0fd134a..59f2cdb745ca 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt @@ -224,7 +224,7 @@ object UserSwitcherViewBinder { } sectionView.removeAllViewsInLayout() - for (viewModel in section) { + section.onEachIndexed { index, viewModel -> val view = layoutInflater.inflate( R.layout.user_switcher_fullscreen_popup_item, @@ -237,6 +237,13 @@ object UserSwitcherViewBinder { view.resources.getString(viewModel.textResourceId) view.setOnClickListener { viewModel.onClicked() } sectionView.addView(view) + // Ensure that the first item in the first section gets accessibility focus. + // Request for focus with a delay when view is inflated an added to the listview. + if (index == 0 && position == 0) { + view.postDelayed({ + view.requestAccessibilityFocus() + }, 200) + } } return sectionView } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt new file mode 100644 index 000000000000..891ee0cf66d7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import android.annotation.WorkerThread +import android.content.pm.ComponentInfo +import android.content.pm.PackageManager +import com.android.systemui.util.Assert + +@WorkerThread +fun PackageManager.isComponentActuallyEnabled(componentInfo: ComponentInfo): Boolean { + Assert.isNotMainThread() + return when (getComponentEnabledSetting(componentInfo.componentName)) { + PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> true + PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> false + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT -> componentInfo.isEnabled + else -> false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index b24a69292186..f37a9b5842a7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -390,8 +390,6 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, public void init(int windowType, Callback callback) { initDialog(mActivityManager.getLockTaskModeState()); - mAccessibility.init(); - mController.addCallback(mControllerCallbackH, mHandler); mController.getState(); @@ -478,8 +476,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); - mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); @@ -677,6 +674,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, initRingerH(); initSettingsH(lockTaskModeState); initODICaptionsH(); + mAccessibility.init(); } private boolean isWindowGravityLeft() { @@ -930,6 +928,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, showRingerDrawer(); } }); + updateSelectedRingerContainerDescription(mIsRingerDrawerOpen); mRingerDrawerVibrate.setOnClickListener( new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE)); @@ -992,6 +991,19 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, : 0; } + @VisibleForTesting String getSelectedRingerContainerDescription() { + return mSelectedRingerContainer == null ? null : + mSelectedRingerContainer.getContentDescription().toString(); + } + + @VisibleForTesting void toggleRingerDrawer(boolean show) { + if (show) { + showRingerDrawer(); + } else { + hideRingerDrawer(); + } + } + /** Animates in the ringer drawer. */ private void showRingerDrawer() { if (mIsRingerDrawerOpen) { @@ -1069,12 +1081,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .start(); } - // When the ringer drawer is open, tapping the currently selected ringer will set the ringer - // to the current ringer mode. Change the content description to that, instead of the 'tap - // to change ringer mode' default. - mSelectedRingerContainer.setContentDescription( - mContext.getString(getStringDescriptionResourceForRingerMode( - mState.ringerModeInternal))); + updateSelectedRingerContainerDescription(true); mIsRingerDrawerOpen = true; } @@ -1120,14 +1127,38 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, .translationY(0f) .start(); - // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the - // user to change the ringer. - mSelectedRingerContainer.setContentDescription( - mContext.getString(R.string.volume_ringer_change)); + updateSelectedRingerContainerDescription(false); mIsRingerDrawerOpen = false; } + + /** + * @param open false to set the description when drawer is closed + */ + private void updateSelectedRingerContainerDescription(boolean open) { + if (mState == null || mSelectedRingerContainer == null) return; + + String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode( + mState.ringerModeInternal)); + String tapToSelect; + + if (open) { + // When the ringer drawer is open, tapping the currently selected ringer will set the + // ringer to the current ringer mode. Change the content description to that, instead of + // the 'tap to change ringer mode' default. + tapToSelect = ""; + + } else { + // When the drawer is closed, tapping the selected ringer drawer will open it, allowing + // the user to change the ringer. The user needs to know that, and also the current mode + currentMode += ", "; + tapToSelect = mContext.getString(R.string.volume_ringer_change); + } + + mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect); + } + private void initSettingsH(int lockTaskModeState) { if (mSettingsView != null) { mSettingsView.setVisibility( @@ -1703,7 +1734,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, }); } - private int getStringDescriptionResourceForRingerMode(int mode) { + @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) { switch (mode) { case RINGER_MODE_SILENT: return R.string.volume_ringer_status_silent; @@ -1785,6 +1816,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, updateVolumeRowH(row); } updateRingerH(); + updateSelectedRingerContainerDescription(mIsRingerDrawerOpen); mWindow.setTitle(composeWindowTitle()); } @@ -2006,14 +2038,14 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, if (row.anim == null) { row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress); row.anim.setInterpolator(new DecelerateInterpolator()); + row.anim.addListener( + getJankListener(row.view, TYPE_UPDATE, UPDATE_ANIMATION_DURATION)); } else { row.anim.cancel(); row.anim.setIntValues(progress, newProgress); } row.animTargetProgress = newProgress; row.anim.setDuration(UPDATE_ANIMATION_DURATION); - row.anim.addListener( - getJankListener(row.view, TYPE_UPDATE, UPDATE_ANIMATION_DURATION)); row.anim.start(); } else { // update slider directly to clamped value diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 254f9531ef83..061340e385a5 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -72,6 +72,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { private FrameLayout mSmallClockFrame; private FrameLayout mLargeClockFrame; + private KeyguardStatusAreaView mStatusArea; KeyguardClockSwitch mKeyguardClockSwitch; @@ -109,6 +110,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { (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); + mStatusArea = mKeyguardClockSwitch.findViewById(R.id.keyguard_status_area); mKeyguardClockSwitch.mChildrenAreLaidOut = true; } @@ -185,6 +187,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); @@ -206,6 +209,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { mKeyguardClockSwitch.mClockInAnim.end(); mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(1); assertThat(mSmallClockFrame.getVisibility()).isEqualTo(VISIBLE); @@ -226,6 +230,31 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @Test + public void switchingToSmallClockAnimation_resetsStatusArea() { + mKeyguardClockSwitch.switchToClock(SMALL, true); + + mKeyguardClockSwitch.mClockInAnim.end(); + mKeyguardClockSwitch.mClockOutAnim.end(); + mKeyguardClockSwitch.mStatusAreaAnim.end(); + + assertThat(mStatusArea.getTranslationX()).isEqualTo(0); + assertThat(mStatusArea.getTranslationY()).isEqualTo(0); + assertThat(mStatusArea.getScaleX()).isEqualTo(1); + assertThat(mStatusArea.getScaleY()).isEqualTo(1); + } + + @Test + public void switchingToSmallClockNoAnimation_resetsStatusArea() { + mKeyguardClockSwitch.switchToClock(SMALL, false); + + assertThat(mStatusArea.getTranslationX()).isEqualTo(0); + assertThat(mStatusArea.getTranslationY()).isEqualTo(0); + assertThat(mStatusArea.getScaleX()).isEqualTo(1); + assertThat(mStatusArea.getScaleY()).isEqualTo(1); + } + + + @Test public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() { assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue(); assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index d2e5a45c3a93..2f20f76ccc50 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -585,21 +585,6 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { } @Test - public void testSecurityCallbackFinish() { - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - when(mKeyguardUpdateMonitor.isUserUnlocked(0)).thenReturn(true); - mKeyguardSecurityContainerController.finish(true, 0); - verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt()); - } - - @Test - public void testSecurityCallbackFinish_cannotDismissLockScreenAndNotStrongAuth() { - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false); - mKeyguardSecurityContainerController.finish(false, 0); - verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()); - } - - @Test public void testOnStartingToHide() { mKeyguardSecurityContainerController.onStartingToHide(); verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 565fc57e81b7..f6450a4fb563 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -253,11 +253,11 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { getViewConstraint(mSecurityViewFlipper.getId()); ConstraintSet.Constraint userSwitcherConstraint = getViewConstraint(R.id.keyguard_bouncer_user_switcher); - assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID); - assertThat(viewFlipperConstraint.layout.leftToRight).isEqualTo( + assertThat(viewFlipperConstraint.layout.endToEnd).isEqualTo(PARENT_ID); + assertThat(viewFlipperConstraint.layout.startToEnd).isEqualTo( R.id.keyguard_bouncer_user_switcher); - assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID); - assertThat(userSwitcherConstraint.layout.rightToLeft).isEqualTo( + assertThat(userSwitcherConstraint.layout.startToStart).isEqualTo(PARENT_ID); + assertThat(userSwitcherConstraint.layout.endToStart).isEqualTo( mSecurityViewFlipper.getId()); assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID); assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID); @@ -377,7 +377,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { ConstraintSet.Constraint viewFlipperConstraint = getViewConstraint( mSecurityViewFlipper.getId()); - assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID); + assertThat(viewFlipperConstraint.layout.startToStart).isEqualTo(PARENT_ID); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt new file mode 100644 index 000000000000..e6b696454d42 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusAreaViewTest.kt @@ -0,0 +1,44 @@ +package com.android.keyguard + +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import com.android.systemui.SysuiTestCase +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +class KeyguardStatusAreaViewTest : SysuiTestCase() { + + private lateinit var view: KeyguardStatusAreaView + + @Before + fun setUp() { + view = KeyguardStatusAreaView(context) + } + + @Test + fun checkTranslationX_AddedTotals() { + view.translateXFromClockDesign = 10f + assertEquals(10f, view.translationX) + + view.translateXFromAod = 20f + assertEquals(30f, view.translationX) + + view.translateXFromUnfold = 30f + assertEquals(60f, view.translationX) + } + + @Test + fun checkTranslationY_AddedTotals() { + view.translateYFromClockSize = 10f + assertEquals(10f, view.translationY) + + view.translateYFromClockDesign = 20f + assertEquals(30f, view.translationY) + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 419d045885a8..de306d669476 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -2800,6 +2800,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { ); } + @Test + public void testOnSimStateChanged_Unknown() { + KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = spy( + KeyguardUpdateMonitorCallback.class); + mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback); + mKeyguardUpdateMonitor.handleSimStateChange(-1, 0, TelephonyManager.SIM_STATE_UNKNOWN); + verify(keyguardUpdateMonitorCallback).onSimStateChanged(-1, 0, + TelephonyManager.SIM_STATE_UNKNOWN); + } + private void verifyFingerprintAuthenticateNeverCalled() { verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt index 57a355f4e127..5e1a8e1432dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt @@ -37,7 +37,7 @@ class FontInterpolatorTest : SysuiTestCase() { private fun assertSameAxes(expect: Font, actual: Font) { val expectAxes = expect.axes?.also { it.sortBy { axis -> axis.tag } } val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } } - assertThat(expectAxes).isEqualTo(actualAxes) + assertThat(actualAxes).isEqualTo(expectAxes) } private fun assertSameAxes(expectVarSettings: String, actual: Font) { @@ -46,7 +46,7 @@ class FontInterpolatorTest : SysuiTestCase() { it.sortBy { axis -> axis.tag } } val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } } - assertThat(expectAxes).isEqualTo(actualAxes) + assertThat(actualAxes).isEqualTo(expectAxes) } @Test @@ -61,7 +61,7 @@ class FontInterpolatorTest : SysuiTestCase() { val interp = FontInterpolator() assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f)) - assertSameAxes("'wght' 496, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) + assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) } @Test @@ -74,7 +74,7 @@ class FontInterpolatorTest : SysuiTestCase() { .build() val interp = FontInterpolator() - assertSameAxes("'wght' 249, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) + assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) } @Test @@ -118,7 +118,7 @@ class FontInterpolatorTest : SysuiTestCase() { .setFontVariationSettings("'wght' 1") .build() val resultFont = interp.lerp(startFont, endFont, 0.5f) - for (i in 0..FONT_CACHE_MAX_ENTRIES + 1) { + for (i in 0..interp.cacheMaxEntries + 1) { val f1 = Font.Builder(sFont) .setFontVariationSettings("'wght' ${i * 100}") .build() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt index 14ad3acf7fb0..263d3750c657 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt @@ -26,18 +26,17 @@ import android.text.TextPaint import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat +import kotlin.math.ceil import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor -import org.mockito.Mockito.`when` import org.mockito.Mockito.eq import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify - -import kotlin.math.ceil +import org.mockito.Mockito.`when` @RunWith(AndroidTestingRunner::class) @SmallTest @@ -56,15 +55,13 @@ class TextAnimatorTest : SysuiTestCase() { val paint = mock(TextPaint::class.java) `when`(textInterpolator.targetPaint).thenReturn(paint) - val textAnimator = TextAnimator(layout, {}).apply { - this.textInterpolator = textInterpolator - this.animator = valueAnimator - } + val textAnimator = + TextAnimator(layout, null, {}).apply { + this.textInterpolator = textInterpolator + this.animator = valueAnimator + } - textAnimator.setTextStyle( - weight = 400, - animate = true - ) + textAnimator.setTextStyle(weight = 400, animate = true) // If animation is requested, the base state should be rebased and the target state should // be updated. @@ -88,15 +85,13 @@ class TextAnimatorTest : SysuiTestCase() { val paint = mock(TextPaint::class.java) `when`(textInterpolator.targetPaint).thenReturn(paint) - val textAnimator = TextAnimator(layout, {}).apply { - this.textInterpolator = textInterpolator - this.animator = valueAnimator - } + val textAnimator = + TextAnimator(layout, null, {}).apply { + this.textInterpolator = textInterpolator + this.animator = valueAnimator + } - textAnimator.setTextStyle( - weight = 400, - animate = false - ) + textAnimator.setTextStyle(weight = 400, animate = false) // If animation is not requested, the progress should be 1 which is end of animation and the // base state is rebased to target state by calling rebase. @@ -118,15 +113,16 @@ class TextAnimatorTest : SysuiTestCase() { `when`(textInterpolator.targetPaint).thenReturn(paint) val animationEndCallback = mock(Runnable::class.java) - val textAnimator = TextAnimator(layout, {}).apply { - this.textInterpolator = textInterpolator - this.animator = valueAnimator - } + val textAnimator = + TextAnimator(layout, null, {}).apply { + this.textInterpolator = textInterpolator + this.animator = valueAnimator + } textAnimator.setTextStyle( - weight = 400, - animate = true, - onAnimationEnd = animationEndCallback + weight = 400, + animate = true, + onAnimationEnd = animationEndCallback ) // Verify animationEnd callback has been added. @@ -144,34 +140,27 @@ class TextAnimatorTest : SysuiTestCase() { val layout = makeLayout("Hello, World", PAINT) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = TextPaint().apply { - typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf") - } + val paint = + TextPaint().apply { + typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf") + } `when`(textInterpolator.targetPaint).thenReturn(paint) - val textAnimator = TextAnimator(layout, {}).apply { - this.textInterpolator = textInterpolator - this.animator = valueAnimator - } + val textAnimator = + TextAnimator(layout, null, {}).apply { + this.textInterpolator = textInterpolator + this.animator = valueAnimator + } - textAnimator.setTextStyle( - weight = 400, - animate = true - ) + textAnimator.setTextStyle(weight = 400, animate = true) val prevTypeface = paint.typeface - textAnimator.setTextStyle( - weight = 700, - animate = true - ) + textAnimator.setTextStyle(weight = 700, animate = true) assertThat(paint.typeface).isNotSameInstanceAs(prevTypeface) - textAnimator.setTextStyle( - weight = 400, - animate = true - ) + textAnimator.setTextStyle(weight = 400, animate = true) assertThat(paint.typeface).isSameInstanceAs(prevTypeface) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt index 2d1e8a830fc9..a93af7dd7450 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt @@ -99,7 +99,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { waitForIdleSync() assertThat(biometricView.isAuthenticated).isTrue() - verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) + verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index 40d9009ee81d..2908e753c2fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -64,7 +64,6 @@ import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintA import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.recents.OverviewProxyService import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock @@ -111,7 +110,6 @@ class SideFpsControllerTest : SysuiTestCase() { @Mock lateinit var activityTaskManager: ActivityTaskManager @Mock lateinit var sideFpsView: View @Mock lateinit var displayManager: DisplayManager - @Mock lateinit var overviewProxyService: OverviewProxyService @Mock lateinit var handler: Handler @Mock lateinit var dumpManager: DumpManager @Captor lateinit var overlayCaptor: ArgumentCaptor<View> @@ -262,7 +260,6 @@ class SideFpsControllerTest : SysuiTestCase() { fingerprintManager, windowManager, activityTaskManager, - overviewProxyService, displayManager, displayStateInteractor, executor, @@ -686,18 +683,6 @@ class SideFpsControllerTest : SysuiTestCase() { verify(windowManager).removeView(any()) } - private fun hidesWithTaskbar(visible: Boolean) { - overlayController.show(SENSOR_ID, REASON_UNKNOWN) - executor.runAllReady() - - sideFpsController.overviewProxyListener.onTaskbarStatusUpdated(true, false) - executor.runAllReady() - - verify(windowManager).addView(any(), any()) - verify(windowManager, never()).removeView(any()) - verify(sideFpsView).visibility = if (visible) View.VISIBLE else View.GONE - } - /** * {@link SideFpsController#updateOverlayParams} calculates indicator placement for ROTATION_0, * and uses RotateUtils.rotateBounds to map to the correct indicator location given the device diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt index 7fb088eb783f..bd4e8da6b326 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt @@ -20,6 +20,7 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -27,16 +28,20 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest class ControlsTileResourceConfigurationImplTest : SysuiTestCase() { + + @Test + fun getPackageName() { + assertThat(ControlsTileResourceConfigurationImpl().getPackageName()).isNull() + } + @Test fun getTileImageId() { val instance = ControlsTileResourceConfigurationImpl() - assertEquals(instance.getTileImageId(), - R.drawable.controls_icon) + assertEquals(instance.getTileImageId(), R.drawable.controls_icon) } @Test fun getTileTitleId() { val instance = ControlsTileResourceConfigurationImpl() - assertEquals(instance.getTileTitleId(), - R.string.quick_controls_title) + assertEquals(instance.getTileTitleId(), R.string.quick_controls_title) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt index 9144b13c7f3e..0b27bc9c7dbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -22,10 +22,10 @@ import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT import com.android.systemui.SysuiTestCase -import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.ControlsTileResourceConfiguration import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.settings.FakeControlsSettingsRepository import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.KeyguardStateController @@ -40,32 +40,25 @@ import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.`when` -import org.mockito.Mockito.any import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) class ControlsComponentTest : SysuiTestCase() { - @Mock - private lateinit var controller: ControlsController - @Mock - private lateinit var uiController: ControlsUiController - @Mock - private lateinit var listingController: ControlsListingController - @Mock - private lateinit var keyguardStateController: KeyguardStateController - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private lateinit var userTracker: UserTracker - @Mock - private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var controller: ControlsController + @Mock private lateinit var uiController: ControlsUiController + @Mock private lateinit var listingController: ControlsListingController + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var userTracker: UserTracker + @Mock private lateinit var lockPatternUtils: LockPatternUtils @Mock private lateinit var optionalControlsTileResourceConfiguration: - Optional<ControlsTileResourceConfiguration> - @Mock - private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration + Optional<ControlsTileResourceConfiguration> + @Mock private lateinit var controlsTileResourceConfiguration: ControlsTileResourceConfiguration private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository @@ -123,8 +116,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCannotShowOnLockScreenVisibility() { - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) controlsSettingsRepository.setCanShowControlsInLockscreen(false) val component = setupComponent(true) @@ -134,8 +126,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCanShowOnLockScreenVisibility() { - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(false) controlsSettingsRepository.setCanShowControlsInLockscreen(true) val component = setupComponent(true) @@ -146,8 +137,7 @@ class ControlsComponentTest : SysuiTestCase() { @Test fun testFeatureEnabledAndCanShowWhileUnlockedVisibility() { controlsSettingsRepository.setCanShowControlsInLockscreen(false) - `when`(lockPatternUtils.getStrongAuthForUser(anyInt())) - .thenReturn(STRONG_AUTH_NOT_REQUIRED) + `when`(lockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED) `when`(keyguardStateController.isUnlocked()).thenReturn(true) val component = setupComponent(true) @@ -158,8 +148,7 @@ class ControlsComponentTest : SysuiTestCase() { fun testGetTileImageId() { val tileImageId = 0 - `when`(controlsTileResourceConfiguration.getTileImageId()) - .thenReturn(tileImageId) + `when`(controlsTileResourceConfiguration.getTileImageId()).thenReturn(tileImageId) val component = setupComponent(true) assertEquals(component.getTileImageId(), tileImageId) } @@ -168,12 +157,19 @@ class ControlsComponentTest : SysuiTestCase() { fun testGetTileTitleId() { val tileTitleId = 0 - `when`(controlsTileResourceConfiguration.getTileTitleId()) - .thenReturn(tileTitleId) + `when`(controlsTileResourceConfiguration.getTileTitleId()).thenReturn(tileTitleId) val component = setupComponent(true) assertEquals(component.getTileTitleId(), tileTitleId) } + @Test + fun getPackageName() { + val packageName = "packageName" + `when`(controlsTileResourceConfiguration.getPackageName()).thenReturn(packageName) + val component = setupComponent(true) + assertEquals(component.getPackageName(), packageName) + } + private fun setupComponent(enabled: Boolean): ControlsComponent { return ControlsComponent( enabled, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index 8ee7d3e86265..cdc99af3cbfe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffor import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository @@ -98,6 +99,7 @@ class CustomizationProviderTest : SysuiTestCase() { @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository private lateinit var underTest: CustomizationProvider private lateinit var testScope: TestScope @@ -111,6 +113,7 @@ class CustomizationProviderTest : SysuiTestCase() { whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() underTest = CustomizationProvider() val testDispatcher = UnconfinedTestDispatcher() @@ -197,6 +200,7 @@ class CustomizationProviderTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = mContext, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index 477e076669b7..22308414547a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.keyguard import android.app.ActivityManager +import android.app.WallpaperManager import android.app.WindowConfiguration import android.graphics.Point import android.graphics.Rect @@ -21,6 +22,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argThat import com.android.systemui.util.mockito.whenever import junit.framework.Assert.assertEquals @@ -32,6 +34,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times @@ -64,6 +67,8 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { private lateinit var notificationShadeWindowController: NotificationShadeWindowController @Mock private lateinit var powerManager: PowerManager + @Mock + private lateinit var wallpaperManager: WallpaperManager @Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub @@ -94,13 +99,14 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { keyguardUnlockAnimationController = KeyguardUnlockAnimationController( context, keyguardStateController, { keyguardViewMediator }, keyguardViewController, featureFlags, { biometricUnlockController }, statusBarStateController, - notificationShadeWindowController, powerManager + notificationShadeWindowController, powerManager, wallpaperManager ) keyguardUnlockAnimationController.setLauncherUnlockController( launcherUnlockAnimationController) whenever(keyguardViewController.viewRootImpl).thenReturn(mock(ViewRootImpl::class.java)) whenever(powerManager.isInteractive).thenReturn(true) + whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false) // All of these fields are final, so we can't mock them, but are needed so that the surface // appear amount setter doesn't short circuit. @@ -173,6 +179,46 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { false /* cancelled */) } + @Test + fun onWakeAndUnlock_notifiesListenerWithTrue() { + whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true) + whenever(biometricUnlockController.mode).thenReturn( + BiometricUnlockController.MODE_WAKE_AND_UNLOCK) + + val listener = mock( + KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java) + keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTargets, + wallpaperTargets, + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ + ) + + verify(listener).onUnlockAnimationStarted(any(), eq(true), any(), any()) + } + + @Test + fun onWakeAndUnlockFromDream_notifiesListenerWithFalse() { + whenever(biometricUnlockController.isWakeAndUnlock).thenReturn(true) + whenever(biometricUnlockController.mode).thenReturn( + BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM) + + val listener = mock( + KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener::class.java) + keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(listener) + + keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation( + remoteAnimationTargets, + wallpaperTargets, + 0 /* startTime */, + false /* requestedShowSurfaceBehindKeyguard */ + ) + + verify(listener).onUnlockAnimationStarted(any(), eq(false), any(), any()) + } + /** * If we requested that the surface behind be made visible, and we're not flinging away the * keyguard, it means that we're swiping to unlock and want the surface visible so it can follow diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt index 13d1e6488d25..c85c7f678536 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt @@ -28,7 +28,7 @@ import kotlinx.coroutines.yield @RoboPilotTest class FakeKeyguardQuickAffordanceConfig( override val key: String, - override val pickerName: String = key, + private val pickerName: String = key, override val pickerIconResourceId: Int = 0, ) : KeyguardQuickAffordanceConfig { @@ -41,6 +41,8 @@ class FakeKeyguardQuickAffordanceConfig( override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = _lockScreenState + override fun pickerName(): String = pickerName + override fun onTriggered( expandable: Expandable?, ): OnTriggeredResult { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index f8cb40885d21..1815ea9530e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.app.Activity import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase @@ -24,6 +25,7 @@ import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.management.ControlsListingController +import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -75,6 +77,7 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes @Mock private lateinit var component: ControlsComponent @Mock private lateinit var controlsController: ControlsController @Mock private lateinit var controlsListingController: ControlsListingController + @Mock private lateinit var controlsUiController: ControlsUiController @Mock private lateinit var controlsServiceInfo: ControlsServiceInfo @Captor private lateinit var callbackCaptor: @@ -98,6 +101,8 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes whenever(component.getControlsController()).thenReturn(Optional.of(controlsController)) whenever(component.getControlsListingController()) .thenReturn(Optional.of(controlsListingController)) + whenever(controlsUiController.resolveActivity()).thenReturn(FakeActivity::class.java) + whenever(component.getControlsUiController()).thenReturn(Optional.of(controlsUiController)) if (hasPanels) { whenever(controlsServiceInfo.panelActivity).thenReturn(mock()) } @@ -178,4 +183,6 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes ) job.cancel() } + + private class FakeActivity : Activity() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt new file mode 100644 index 000000000000..7941a23fe30d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.quickaffordance + +import android.content.Intent +import android.net.Uri +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardQuickAffordanceConfigTest : SysuiTestCase() { + + @Test + fun appStoreIntent() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNotNull() + assertThat(intent?.`package`).isEqualTo("app.store.package.name") + assertThat(intent?.action).isEqualTo(Intent.ACTION_VIEW) + assertThat(intent?.data).isEqualTo(Uri.parse("prefix?id=$packageName")) + } + + @Test + fun appStoreIntent_packageNameNotConfigured_returnNull() { + overrideResource(R.string.config_appStorePackageName, "") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNull() + } + + @Test(expected = IllegalStateException::class) + fun appStoreIntent_packageNameMisconfigured_throwsIllegalStateException() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource( + R.string.config_appStoreAppLinkTemplate, + "prefix?id=\$misconfiguredPackageName" + ) + val packageName = "com.app.package.name" + + KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + } + + @Test + fun appStoreIntent_linkTemplateNotConfigured_returnNull() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "") + val packageName = "com.app.package.name" + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, packageName) + + assertThat(intent).isNull() + } + + @Test + fun appStoreIntent_appPackageNameNull_returnNull() { + overrideResource(R.string.config_appStorePackageName, "app.store.package.name") + overrideResource(R.string.config_appStoreAppLinkTemplate, "prefix?id=\$packageName") + + val intent = KeyguardQuickAffordanceConfig.appStoreIntent(context, null) + + assertThat(intent).isNull() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 26c0ea4bd11a..7510373ed582 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -53,7 +53,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) whenever(controller.intent).thenReturn(INTENT_1) - underTest = QrCodeScannerKeyguardQuickAffordanceConfig(mock(), controller) + underTest = QrCodeScannerKeyguardQuickAffordanceConfig(context, controller) } @Test @@ -77,22 +77,21 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = - runBlockingTest { - whenever(controller.isEnabledForLockScreenButton).thenReturn(true) - var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null - val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) - val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() - verify(controller).addCallback(callbackCaptor.capture()) - - whenever(controller.intent).thenReturn(INTENT_2) - callbackCaptor.value.onQRCodeScannerActivityChanged() - - assertVisibleState(latest) - - job.cancel() - verify(controller).removeCallback(callbackCaptor.value) - } + fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = runBlockingTest { + whenever(controller.isEnabledForLockScreenButton).thenReturn(true) + var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null + val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) + val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>() + verify(controller).addCallback(callbackCaptor.capture()) + + whenever(controller.intent).thenReturn(INTENT_2) + callbackCaptor.value.onQRCodeScannerActivityChanged() + + assertVisibleState(latest) + + job.cancel() + verify(controller).removeCallback(callbackCaptor.value) + } @Test fun affordance_scannerPreferenceChanged_deliversVisibleModel() = runBlockingTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index 111b8e83a984..d36e77889810 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -92,8 +92,8 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun affordance_walletNotEnabled_modelIsNone() = runBlockingTest { - setUpState(isWalletEnabled = false) + fun affordance_walletFeatureNotEnabled_modelIsNone() = runBlockingTest { + setUpState(isWalletFeatureAvailable = false) var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this) @@ -165,7 +165,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { @Test fun getPickerScreenState_disabledWhenTheFeatureIsNotEnabled() = runTest { setUpState( - isWalletEnabled = false, + isWalletFeatureAvailable = false, ) assertThat(underTest.getPickerScreenState()) @@ -183,16 +183,15 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } private fun setUpState( - isWalletEnabled: Boolean = true, + isWalletFeatureAvailable: Boolean = true, isWalletServiceAvailable: Boolean = true, isWalletQuerySuccessful: Boolean = true, hasSelectedCard: Boolean = true, ) { - whenever(walletController.isWalletEnabled).thenReturn(isWalletEnabled) - val walletClient: QuickAccessWalletClient = mock() whenever(walletClient.tileIcon).thenReturn(ICON) whenever(walletClient.isWalletServiceAvailable).thenReturn(isWalletServiceAvailable) + whenever(walletClient.isWalletFeatureAvailable).thenReturn(isWalletFeatureAvailable) whenever(walletController.walletClient).thenReturn(walletClient) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index ca7c5db478a1..6b5be58b6d03 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -189,12 +189,12 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = config1.key, - name = config1.pickerName, + name = config1.pickerName(), iconResourceId = config1.pickerIconResourceId, ), KeyguardQuickAffordancePickerRepresentation( id = config2.key, - name = config2.pickerName, + name = config2.pickerName(), iconResourceId = config2.pickerIconResourceId, ), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index fb21847cd4d1..2c90d530194d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository @@ -239,6 +240,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @JvmField @Parameter(4) var startActivity: Boolean = false private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository private lateinit var userTracker: UserTracker @Before @@ -250,6 +252,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { homeControls = FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() val quickAccessWallet = FakeKeyguardQuickAffordanceConfig( BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET @@ -339,6 +342,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = mContext, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 895c1cd5a1a1..7cab680ef3e6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -40,6 +40,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository @@ -97,6 +98,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig private lateinit var featureFlags: FakeFeatureFlags private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Before fun setUp() { @@ -119,6 +121,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { testScope = TestScope(testDispatcher) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() val localUserSelectionManager = KeyguardQuickAffordanceLocalUserSelectionManager( @@ -201,6 +204,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = context, ) @@ -314,6 +318,24 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun quickAffordance_hiddenWhenUserIsInLockdownMode() = + testScope.runTest { + biometricSettingsRepository.setIsUserInLockdown(true) + quickAccessWallet.setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = ICON, + ) + ) + + val collectedValue by + collectLastValue( + underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END) + ) + + assertThat(collectedValue).isEqualTo(KeyguardQuickAffordanceModel.Hidden) + } + + @Test fun quickAffordance_bottomStartAffordanceHiddenWhileDozing() = testScope.runTest { repository.setIsDozing(true) @@ -435,7 +457,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = homeControls.key, - name = homeControls.pickerName, + name = homeControls.pickerName(), iconResourceId = homeControls.pickerIconResourceId, ), ), @@ -469,7 +491,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = quickAccessWallet.key, - name = quickAccessWallet.pickerName, + name = quickAccessWallet.pickerName(), iconResourceId = quickAccessWallet.pickerIconResourceId, ), ), @@ -506,7 +528,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = quickAccessWallet.key, - name = quickAccessWallet.pickerName, + name = quickAccessWallet.pickerName(), iconResourceId = quickAccessWallet.pickerIconResourceId, ), ), @@ -514,7 +536,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = qrCodeScanner.key, - name = qrCodeScanner.pickerName, + name = qrCodeScanner.pickerName(), iconResourceId = qrCodeScanner.pickerIconResourceId, ), ), @@ -570,7 +592,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = quickAccessWallet.key, - name = quickAccessWallet.pickerName, + name = quickAccessWallet.pickerName(), iconResourceId = quickAccessWallet.pickerIconResourceId, ), ), @@ -665,7 +687,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { listOf( KeyguardQuickAffordancePickerRepresentation( id = quickAccessWallet.key, - name = quickAccessWallet.pickerName, + name = quickAccessWallet.pickerName(), iconResourceId = quickAccessWallet.pickerIconResourceId, ), ), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index 8a36dbc86e81..73fe380f01c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager +import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -110,6 +111,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var qrCodeScannerAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var dockManager: DockManagerFake + private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository @Before fun setUp() { @@ -126,6 +128,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { qrCodeScannerAffordanceConfig = FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) dockManager = DockManagerFake() + biometricSettingsRepository = FakeBiometricSettingsRepository() registry = FakeKeyguardQuickAffordanceRegistry( mapOf( @@ -240,6 +243,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { logger = logger, devicePolicyManager = devicePolicyManager, dockManager = dockManager, + biometricSettingsRepository = biometricSettingsRepository, backgroundDispatcher = testDispatcher, appContext = mContext, ), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt index 97b18e214550..d6ceea11b801 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/models/player/SeekBarObserverTest.kt @@ -234,4 +234,17 @@ class SeekBarObserverTest : SysuiTestCase() { assertThat(seekBarView.progress).isEqualTo(4000) verify(mockSeekbarAnimator).start() } + + @Test + fun seekbarActive_animationsDisabled() { + // WHEN playing, but animations have been disabled + observer.animationEnabled = false + val isPlaying = true + val isScrubbing = false + val data = SeekBarViewModel.Progress(true, true, isPlaying, isScrubbing, 3000, 120000) + observer.onChanged(data) + + // THEN progress drawable does not animate + verify(mockSquigglyProgress).animate = false + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index f030a03be80f..f6075add6afe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -25,6 +25,7 @@ import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.res.Configuration +import android.database.ContentObserver import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color @@ -40,6 +41,7 @@ import android.media.MediaMetadata import android.media.session.MediaSession import android.media.session.PlaybackState import android.os.Bundle +import android.provider.Settings import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -73,6 +75,7 @@ import com.android.systemui.media.controls.models.player.MediaButton import com.android.systemui.media.controls.models.player.MediaData import com.android.systemui.media.controls.models.player.MediaDeviceData import com.android.systemui.media.controls.models.player.MediaViewHolder +import com.android.systemui.media.controls.models.player.SeekBarObserver import com.android.systemui.media.controls.models.player.SeekBarViewModel import com.android.systemui.media.controls.models.recommendation.KEY_SMARTSPACE_APP_NAME import com.android.systemui.media.controls.models.recommendation.RecommendationViewHolder @@ -97,6 +100,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import dagger.Lazy @@ -109,6 +113,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.anyString import org.mockito.Mockito.mock @@ -233,6 +238,8 @@ public class MediaControlPanelTest : SysuiTestCase() { this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true) this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false) } + @Mock private lateinit var globalSettings: GlobalSettings + @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> @JvmField @Rule val mockito = MockitoJUnit.rule() @@ -273,7 +280,8 @@ public class MediaControlPanelTest : SysuiTestCase() { activityIntentHelper, lockscreenUserManager, broadcastDialogController, - fakeFeatureFlag + fakeFeatureFlag, + globalSettings, ) { override fun loadAnimator( animId: Int, @@ -284,6 +292,12 @@ public class MediaControlPanelTest : SysuiTestCase() { } } + verify(globalSettings) + .registerContentObserver( + eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)), + settingsObserverCaptor.capture() + ) + initGutsViewHolderMocks() initMediaViewHolderMocks() @@ -955,6 +969,30 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun animationSettingChange_updateSeekbar() { + // When animations are enabled + globalSettings.putFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) + val progress = 0.5 + val state = mediaData.copy(resumption = true, resumeProgress = progress) + player.attachPlayer(viewHolder) + player.bindPlayer(state, PACKAGE) + + val captor = argumentCaptor<SeekBarObserver>() + verify(seekBarData).observeForever(captor.capture()) + val seekBarObserver = captor.value!! + + // Then the seekbar is set to animate + assertThat(seekBarObserver.animationEnabled).isTrue() + + // When the setting changes, + globalSettings.putFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 0f) + settingsObserverCaptor.value!!.onChange(false) + + // Then the seekbar is set to not animate + assertThat(seekBarObserver.animationEnabled).isFalse() + } + + @Test fun bindNotificationActions() { val icon = context.getDrawable(android.R.drawable.ic_media_play) val bg = context.getDrawable(R.drawable.qs_media_round_button_background) @@ -2382,7 +2420,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun playTurbulenceNoise_finishesAfterDuration() { - fakeFeatureFlag.set(Flags.UMO_SURFACE_RIPPLE, true) fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true) val semanticActions = @@ -2414,6 +2451,29 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun playTurbulenceNoise_whenPlaybackStateIsNotPlaying_doesNotPlayTurbulence() { + fakeFeatureFlag.set(Flags.UMO_TURBULENCE_NOISE, true) + + val semanticActions = + MediaButton( + custom0 = + MediaAction( + icon = null, + action = {}, + contentDescription = "custom0", + background = null + ), + ) + val data = mediaData.copy(semanticActions = semanticActions) + player.attachPlayer(viewHolder) + player.bindPlayer(data, KEY) + + viewHolder.action0.callOnClick() + + assertThat(turbulenceNoiseView.visibility).isEqualTo(View.INVISIBLE) + } + + @Test fun outputSwitcher_hasCustomIntent_openOverLockscreen() { // When the device for a media player has an intent that opens over lockscreen val pendingIntent = mock(PendingIntent::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index c89897c0233c..6d8c9b106881 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -247,6 +247,20 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test + public void onBindViewHolder_bindConnectedRemoteDevice_verifyContentDescriptionNotNull() { + when(mMediaOutputController.getSelectableMediaDevice()).thenReturn( + ImmutableList.of(mMediaDevice2)); + when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(true); + mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter + .onCreateViewHolder(new LinearLayout(mContext), 0); + mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); + + assertThat(mViewHolder.mSeekBar.getContentDescription()).isNotNull(); + assertThat(mViewHolder.mSeekBar.getAccessibilityDelegate()).isNotNull(); + assertThat(mViewHolder.mContainerLayout.isFocusable()).isFalse(); + } + + @Test public void onBindViewHolder_bindSingleConnectedRemoteDevice_verifyView() { when(mMediaOutputController.getSelectableMediaDevice()).thenReturn( ImmutableList.of()); @@ -334,6 +348,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2); + assertThat(mViewHolder.mContainerLayout.isFocusable()).isTrue(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java index 705b485ce1b4..9dba9b5b3c3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java @@ -16,6 +16,8 @@ package com.android.systemui.media.dialog; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.mock; @@ -33,6 +35,10 @@ import android.media.session.MediaSessionManager; import android.os.PowerExemptionManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; import androidx.test.filters.SmallTest; @@ -44,6 +50,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; import com.android.settingslib.media.BluetoothMediaDevice; import com.android.settingslib.media.LocalMediaManager; import com.android.settingslib.media.MediaDevice; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastSender; @@ -53,11 +60,14 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; +import com.google.common.base.Strings; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -68,6 +78,9 @@ import java.util.Optional; public class MediaOutputBroadcastDialogTest extends SysuiTestCase { private static final String TEST_PACKAGE = "test_package"; + private static final String BROADCAST_NAME_TEST = "Broadcast_name_test"; + private static final String BROADCAST_CODE_TEST = "112233"; + private static final String BROADCAST_CODE_UPDATE_TEST = "11223344"; // Mock private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); @@ -106,6 +119,9 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase { when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null); when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null); + when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST); + when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn( + BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8)); mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, mMediaSessionManager, mLocalBluetoothManager, mStarter, @@ -194,4 +210,152 @@ public class MediaOutputBroadcastDialogTest extends SysuiTestCase { verify(mLocalBluetoothLeBroadcastAssistant, times(1)).addSource(any(), any(), anyBoolean()); } + + @Test + public void handleLeBroadcastMetadataChanged_checkBroadcastName() { + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + final TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_name_summary); + + mMediaOutputBroadcastDialog.handleLeBroadcastMetadataChanged(); + + assertThat(broadcastName.getText().toString()).isEqualTo(BROADCAST_NAME_TEST); + } + + @Test + public void handleLeBroadcastMetadataChanged_checkBroadcastCode() { + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + + final TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_code_summary); + + mMediaOutputBroadcastDialog.handleLeBroadcastMetadataChanged(); + + assertThat(broadcastCode.getText().toString()).isEqualTo(BROADCAST_CODE_TEST); + } + + @Test + public void updateBroadcastInfo_stopBroadcastFailed_handleFailedUi() { + ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_code_edit); + TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_code_summary); + broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null); + broadcastCodeEdit.callOnClick(); + + mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); + assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(1); + + mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); + assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(2); + + // It will be the MAX Retry Count = 3 + mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); + assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(0); + } + + @Test + public void afterTextChanged_nameLengthMoreThanMax_showErrorMessage() { + ImageView broadcastNameEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_name_edit); + TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_name_summary); + broadcastName.setText(BROADCAST_NAME_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastNameEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String moreThanMax = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH + 3); + editText.setText(moreThanMax); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void afterTextChanged_enterValidNameAfterLengthMoreThanMax_noErrorMessage() { + ImageView broadcastNameEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_name_edit); + TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_name_summary); + broadcastName.setText(BROADCAST_NAME_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastNameEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String testString = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH + 2); + editText.setText(testString); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + + // input the valid text + testString = Strings.repeat("b", + MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH - 100); + editText.setText(testString); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + } + + @Test + public void afterTextChanged_codeLengthMoreThanMax_showErrorMessage() { + ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_code_edit); + TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_code_summary); + broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastCodeEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String moreThanMax = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_CODE_MAX_LENGTH + 1); + editText.setText(moreThanMax); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void afterTextChanged_codeLengthLessThanMin_showErrorMessage() { + ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView + .requireViewById(R.id.broadcast_code_edit); + TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( + R.id.broadcast_code_summary); + broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + broadcastCodeEdit.callOnClick(); + EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_edit_text); + TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( + R.id.broadcast_error_message); + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); + + // input the invalid text + String moreThanMax = Strings.repeat("a", + MediaOutputBroadcastDialog.BROADCAST_CODE_MIN_LENGTH - 1); + editText.setText(moreThanMax); + + assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt index f1bbd8434b40..f25cd24dfcb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt @@ -5,14 +5,15 @@ import android.os.UserHandle import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider +import com.android.systemui.mediaprojection.devicepolicy.ScreenCaptureDevicePolicyResolver +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verify @@ -27,23 +28,28 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { private val callerPackageName = "com.test.caller" private val callerComponentName = ComponentName(callerPackageName, "Caller") - private val hostUserHandle = UserHandle.of(123) - private val otherUserHandle = UserHandle.of(456) + private val personalUserHandle = UserHandle.of(123) + private val workUserHandle = UserHandle.of(456) private val view: MediaProjectionAppSelectorView = mock() - private val featureFlags: FeatureFlags = mock() + private val policyResolver: ScreenCaptureDevicePolicyResolver = mock() private val controller = MediaProjectionAppSelectorController( taskListProvider, view, - featureFlags, - hostUserHandle, + policyResolver, + personalUserHandle, scope, appSelectorComponentName, callerPackageName ) + @Before + fun setup() { + givenCaptureAllowed(isAllow = true) + } + @Test fun initNoRecentTasks_bindsEmptyList() { taskListProvider.tasks = emptyList() @@ -132,73 +138,57 @@ class MediaProjectionAppSelectorControllerTest : SysuiTestCase() { } @Test - fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesDisabled_bindsAllTasks() { - givenEnterprisePoliciesFeatureFlag(enabled = false) - + fun initRecentTasksWithAppSelectorTasks_withEnterprisePolicies_bindsAllTasks() { val tasks = listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + createRecentTask(taskId = 1, userId = personalUserHandle.identifier), + createRecentTask(taskId = 2, userId = workUserHandle.identifier), + createRecentTask(taskId = 3, userId = personalUserHandle.identifier), + createRecentTask(taskId = 4, userId = workUserHandle.identifier), + createRecentTask(taskId = 5, userId = personalUserHandle.identifier), ) taskListProvider.tasks = tasks controller.init() - // TODO (b/263950746): Cross-profile filtering is removed for now. This should be brought - // back with the future fix verify(view) .bind( listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + createRecentTask(taskId = 1, userId = personalUserHandle.identifier), + createRecentTask(taskId = 2, userId = workUserHandle.identifier), + createRecentTask(taskId = 3, userId = personalUserHandle.identifier), + createRecentTask(taskId = 4, userId = workUserHandle.identifier), + createRecentTask(taskId = 5, userId = personalUserHandle.identifier), ) ) } @Test - fun initRecentTasksWithAppSelectorTasks_enterprisePoliciesEnabled_bindsAllTasks() { - givenEnterprisePoliciesFeatureFlag(enabled = true) - + fun initRecentTasksWithAppSelectorTasks_withEnterprisePolicies_blocksAllTasks() { val tasks = listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), + createRecentTask(taskId = 1, userId = personalUserHandle.identifier), + createRecentTask(taskId = 2, userId = workUserHandle.identifier), + createRecentTask(taskId = 3, userId = personalUserHandle.identifier), + createRecentTask(taskId = 4, userId = workUserHandle.identifier), + createRecentTask(taskId = 5, userId = personalUserHandle.identifier), ) taskListProvider.tasks = tasks + givenCaptureAllowed(isAllow = false) controller.init() - // TODO(b/233348916) should filter depending on the policies - verify(view) - .bind( - listOf( - createRecentTask(taskId = 1, userId = hostUserHandle.identifier), - createRecentTask(taskId = 2, userId = otherUserHandle.identifier), - createRecentTask(taskId = 3, userId = hostUserHandle.identifier), - createRecentTask(taskId = 4, userId = otherUserHandle.identifier), - createRecentTask(taskId = 5, userId = hostUserHandle.identifier), - ) - ) + verify(view).bind(emptyList()) } - private fun givenEnterprisePoliciesFeatureFlag(enabled: Boolean) { - whenever(featureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES)) - .thenReturn(enabled) + private fun givenCaptureAllowed(isAllow: Boolean) { + whenever(policyResolver.isScreenCaptureAllowed(any(), any())).thenReturn(isAllow) } private fun createRecentTask( taskId: Int, topActivityComponent: ComponentName? = null, - userId: Int = hostUserHandle.identifier + userId: Int = personalUserHandle.identifier ): RecentTask { return RecentTask( taskId = taskId, diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt index e8b6f9bd3478..c63efa1ba38a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt @@ -406,28 +406,6 @@ class IsAllowedScreenCaptureDevicePolicyResolverTest( isHostInWorkProfile = true, isTargetInWorkProfile = true, personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = false - ), - expectedScreenCaptureAllowed = true, - ), - IsScreenCaptureAllowedTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - isTargetInWorkProfile = true, - personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = true - ), - expectedScreenCaptureAllowed = true, - ), - IsScreenCaptureAllowedTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - isTargetInWorkProfile = true, - personalScreenCaptureDisabled = true, workScreenCaptureDisabled = true, disallowShareIntoManagedProfile = false ), @@ -626,26 +604,6 @@ class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest( Preconditions( isHostInWorkProfile = true, personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = false - ), - expectedScreenCaptureCompletelyDisabled = false, - ), - IsScreenCaptureCompletelyDisabledTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - personalScreenCaptureDisabled = true, - workScreenCaptureDisabled = false, - disallowShareIntoManagedProfile = true - ), - expectedScreenCaptureCompletelyDisabled = false, - ), - IsScreenCaptureCompletelyDisabledTestCase( - given = - Preconditions( - isHostInWorkProfile = true, - personalScreenCaptureDisabled = true, workScreenCaptureDisabled = true, disallowShareIntoManagedProfile = false ), @@ -686,7 +644,8 @@ class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest( "personal screen capture disabled = ${given.personalScreenCaptureDisabled}, " + "work screen capture disabled = ${given.workScreenCaptureDisabled}, " + "disallow share into managed profile = ${given.disallowShareIntoManagedProfile}, " + - "expected screen capture completely disabled = $expectedScreenCaptureCompletelyDisabled" + "expected screen capture completely disabled = " + + "$expectedScreenCaptureCompletelyDisabled" } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index e99f8b6aa47b..0954f6f0ffaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -72,6 +72,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.doNothing import org.mockito.Mockito.never import org.mockito.Mockito.spy @@ -227,12 +228,15 @@ internal class NoteTaskControllerTest : SysuiTestCase() { // region showNoteTask @Test - fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() { - val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true) + fun showNoteTaskAsUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() { + val user10 = UserHandle.of(/* userId= */ 10) + val expectedInfo = + NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) - createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) + createNoteTaskController() + .showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10) val intentCaptor = argumentCaptor<Intent>() val userCaptor = argumentCaptor<UserHandle>() @@ -245,21 +249,18 @@ internal class NoteTaskControllerTest : SysuiTestCase() { hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT) extras().bool(EXTRA_USE_STYLUS_MODE).isTrue() } - assertThat(userCaptor.value).isEqualTo(userTracker.userHandle) + assertThat(userCaptor.value).isEqualTo(user10) verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) } @Test - fun showNoteTaskWithUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() { - val user10 = UserHandle.of(/* userId= */ 10) - val expectedInfo = - NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10) + fun showNoteTask_keyguardIsLocked_notesIsClosed_shouldStartActivityAndLogUiEvent() { + val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) - createNoteTaskController() - .showNoteTaskAsUser(entryPoint = expectedInfo.entryPoint!!, user = user10) + createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) val intentCaptor = argumentCaptor<Intent>() val userCaptor = argumentCaptor<UserHandle>() @@ -272,7 +273,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { hasFlags(FLAG_ACTIVITY_NEW_DOCUMENT) extras().bool(EXTRA_USE_STYLUS_MODE).isTrue() } - assertThat(userCaptor.value).isEqualTo(user10) + assertThat(userCaptor.value).isEqualTo(userTracker.userHandle) verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) } @@ -301,15 +302,31 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test - fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesWithoutLoggingUiEvent() { + fun showNoteTask_keyguardIsUnlocked_noteIsClosed_shouldStartBubblesWithoutLoggingUiEvent() { + val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) + whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) + + createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) + + // Context package name used to create bubble icon from drawable resource id + verify(context, atLeastOnce()).packageName + verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle) + verifyZeroInteractions(eventLogger) + } + + @Test + fun showNoteTask_keyguardIsUnlocked_noteIsOpen_shouldStartBubblesWithoutLoggingUiEvent() { val expectedInfo = NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = false) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) + whenever(activityManager.getRunningTasks(anyInt())) + .thenReturn(listOf(NOTE_RUNNING_TASK_INFO)) createNoteTaskController().showNoteTask(entryPoint = expectedInfo.entryPoint!!) // Context package name used to create bubble icon from drawable resource id - verify(context).packageName + verify(context, atLeastOnce()).packageName verifyNoteTaskOpenInBubbleInUser(userTracker.userHandle) verifyZeroInteractions(eventLogger) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt index 34354504abf0..24f39d187f88 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt @@ -16,37 +16,47 @@ package com.android.systemui.notetask import android.os.UserHandle -import android.test.suitebuilder.annotation.SmallTest -import androidx.test.runner.AndroidJUnit4 +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith /** atest SystemUITests:NoteTaskInfoTest */ @SmallTest -@RunWith(AndroidJUnit4::class) +@RunWith(AndroidTestingRunner::class) internal class NoteTaskInfoTest : SysuiTestCase() { - private fun createNoteTaskInfo(): NoteTaskInfo = - NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID, UserHandle.of(0)) - @Test fun launchMode_keyguardLocked_launchModeActivity() { - val underTest = createNoteTaskInfo().copy(isKeyguardLocked = true) + val underTest = DEFAULT_INFO.copy(isKeyguardLocked = true) assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity) } @Test - fun launchMode_keyguardUnlocked_launchModeActivity() { - val underTest = createNoteTaskInfo().copy(isKeyguardLocked = false) + fun launchMode_multiWindowMode_launchModeActivity() { + val underTest = DEFAULT_INFO.copy(entryPoint = WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE) + + assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity) + } + + @Test + fun launchMode_keyguardUnlocked_launchModeAppBubble() { + val underTest = DEFAULT_INFO.copy(isKeyguardLocked = false) assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble) } private companion object { - const val NOTES_PACKAGE_NAME = "com.android.note.app" - const val NOTES_UID = 123456 + + val DEFAULT_INFO = + NoteTaskInfo( + packageName = "com.android.note.app", + uid = 123456, + user = UserHandle.of(0), + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index 452658004733..58c762e3cc99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -40,7 +40,6 @@ import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.C import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint import com.android.systemui.notetask.NoteTaskInfoResolver -import com.android.systemui.shared.customization.data.content.CustomizationProviderContract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR import com.android.systemui.stylus.StylusManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -283,7 +282,7 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { } @Test - fun getPickerScreenState_nodefaultNoteAppSet_shouldReturnDisable() = runTest { + fun getPickerScreenState_noDefaultNoteAppSet_shouldReturnDisabled() = runTest { val underTest = createUnderTest(isEnabled = true) whenever( roleManager.getRoleHoldersAsUser( @@ -293,16 +292,16 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { ) .thenReturn(emptyList()) - assertThat(underTest.getPickerScreenState()) - .isEqualTo( - KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( - listOf("Select a default notes app to use the notetaking shortcut"), - actionText = "Select app", - actionComponentName = - "${context.packageName}$COMPONENT_NAME_SEPARATOR" + - "$ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" - ) - ) + val pickerScreenState = underTest.getPickerScreenState() + assertThat(pickerScreenState is KeyguardQuickAffordanceConfig.PickerScreenState.Disabled) + .isTrue() + val disabled = pickerScreenState as KeyguardQuickAffordanceConfig.PickerScreenState.Disabled + assertThat(disabled.explanation) + .isEqualTo("Select a default notes app to use the notetaking shortcut") + assertThat(disabled.actionText).isEqualTo("Select app") + assertThat(disabled.actionIntent?.action) + .isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) + assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName) } // endregion diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index e0f27de1a6de..e669d0697004 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -481,6 +481,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { int[] widgetIdsArray = {1}; when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + when(mUserManager.isUserUnlocked(any())).thenReturn(true); + NotificationChannel channel = new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); @@ -489,8 +491,25 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); mClock.advanceTime(MIN_LINGER_DURATION); - verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(), - any()); + verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(), any()); + } + + @Test + public void testOnNotificationChannelModified_userLocked() { + int[] widgetIdsArray = {1}; + when(mAppWidgetManager.getAppWidgetIds(any())).thenReturn(widgetIdsArray); + + when(mUserManager.isUserUnlocked(any())).thenReturn(false); + + NotificationChannel channel = + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); + channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); + + mNoMan.issueChannelModification(TEST_PACKAGE_A, + UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); + mClock.advanceTime(MIN_LINGER_DURATION); + + verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt new file mode 100644 index 000000000000..18f3837a7d36 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.repository + +import android.Manifest.permission.BIND_QUICK_SETTINGS_TILE +import android.content.BroadcastReceiver +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.PackageManager.ResolveInfoFlags +import android.content.pm.ResolveInfo +import android.content.pm.ServiceInfo +import android.os.UserHandle +import android.service.quicksettings.TileService +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argThat +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.nullable +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@OptIn(ExperimentalCoroutinesApi::class) +class InstalledTilesComponentRepositoryImplTest : SysuiTestCase() { + private val testDispatcher = StandardTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Mock private lateinit var context: Context + @Mock private lateinit var packageManager: PackageManager + @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> + + private lateinit var underTest: InstalledTilesComponentRepositoryImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + // Use the default value set in the ServiceInfo + whenever(packageManager.getComponentEnabledSetting(any())) + .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) + + // Return empty by default + whenever(packageManager.queryIntentServicesAsUser(any(), any<ResolveInfoFlags>(), anyInt())) + .thenReturn(emptyList()) + + underTest = + InstalledTilesComponentRepositoryImpl( + context, + packageManager, + testDispatcher, + ) + } + + @Test + fun registersAndUnregistersBroadcastReceiver() = + testScope.runTest { + val user = 10 + val job = launch { underTest.getInstalledTilesComponents(user).collect {} } + runCurrent() + + verify(context) + .registerReceiverAsUser( + capture(receiverCaptor), + eq(UserHandle.of(user)), + any(), + nullable(), + nullable(), + ) + + verify(context, never()).unregisterReceiver(receiverCaptor.value) + + job.cancel() + runCurrent() + verify(context).unregisterReceiver(receiverCaptor.value) + } + + @Test + fun intentFilterForCorrectActionsAndScheme() = + testScope.runTest { + val filterCaptor = argumentCaptor<IntentFilter>() + + backgroundScope.launch { underTest.getInstalledTilesComponents(0).collect {} } + runCurrent() + + verify(context) + .registerReceiverAsUser( + any(), + any(), + capture(filterCaptor), + nullable(), + nullable(), + ) + + with(filterCaptor.value) { + assertThat(matchAction(Intent.ACTION_PACKAGE_CHANGED)).isTrue() + assertThat(matchAction(Intent.ACTION_PACKAGE_ADDED)).isTrue() + assertThat(matchAction(Intent.ACTION_PACKAGE_REMOVED)).isTrue() + assertThat(matchAction(Intent.ACTION_PACKAGE_REPLACED)).isTrue() + assertThat(countActions()).isEqualTo(4) + + assertThat(hasDataScheme("package")).isTrue() + assertThat(countDataSchemes()).isEqualTo(1) + } + } + + @Test + fun componentsLoadedOnStart() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true) + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + + assertThat(componentNames).containsExactly(TEST_COMPONENT) + } + + @Test + fun componentAdded_foundAfterBroadcast() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + assertThat(componentNames).isEmpty() + + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + getRegisteredReceiver().onReceive(context, Intent(Intent.ACTION_PACKAGE_ADDED)) + + assertThat(componentNames).containsExactly(TEST_COMPONENT) + } + + @Test + fun componentWithoutPermission_notValid() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = false, defaultEnabled = true) + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + assertThat(componentNames).isEmpty() + } + + @Test + fun componentNotEnabled_notValid() = + testScope.runTest { + val userId = 0 + val resolveInfo = + ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = false) + whenever( + packageManager.queryIntentServicesAsUser( + matchIntent(), + matchFlags(), + eq(userId) + ) + ) + .thenReturn(listOf(resolveInfo)) + + val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId)) + assertThat(componentNames).isEmpty() + } + + private fun getRegisteredReceiver(): BroadcastReceiver { + verify(context) + .registerReceiverAsUser( + capture(receiverCaptor), + any(), + any(), + nullable(), + nullable(), + ) + + return receiverCaptor.value + } + + companion object { + private val INTENT = Intent(TileService.ACTION_QS_TILE) + private val FLAGS = + ResolveInfoFlags.of( + (PackageManager.MATCH_DIRECT_BOOT_AWARE or + PackageManager.MATCH_DIRECT_BOOT_UNAWARE or + PackageManager.GET_SERVICES) + .toLong() + ) + private val PERMISSION = BIND_QUICK_SETTINGS_TILE + + private val TEST_COMPONENT = ComponentName("pkg", "cls") + + private fun matchFlags() = + argThat<ResolveInfoFlags> { flags -> flags?.value == FLAGS.value } + private fun matchIntent() = argThat<Intent> { intent -> intent.action == INTENT.action } + + private fun ResolveInfo( + componentName: ComponentName, + hasPermission: Boolean, + defaultEnabled: Boolean + ): ResolveInfo { + val applicationInfo = ApplicationInfo().apply { enabled = true } + val serviceInfo = + ServiceInfo().apply { + packageName = componentName.packageName + name = componentName.className + if (hasPermission) { + permission = PERMISSION + } + enabled = defaultEnabled + this.applicationInfo = applicationInfo + } + val resolveInfo = ResolveInfo() + resolveInfo.serviceInfo = serviceInfo + return resolveInfo + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 426ff670802f..e7ad4896810b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.qs.external.TileLifecycleManager import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository import com.android.systemui.qs.pipeline.data.repository.FakeCustomTileAddedRepository +import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesComponentRepository import com.android.systemui.qs.pipeline.data.repository.FakeTileSpecRepository import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository import com.android.systemui.qs.pipeline.domain.model.TileModel @@ -73,6 +74,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val tileSpecRepository: TileSpecRepository = FakeTileSpecRepository() private val userRepository = FakeUserRepository() + private val installedTilesPackageRepository = FakeInstalledTilesComponentRepository() private val tileFactory = FakeQSFactory(::tileCreator) private val customTileAddedRepository: CustomTileAddedRepository = FakeCustomTileAddedRepository() @@ -100,11 +102,13 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { featureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true) userRepository.setUserInfos(listOf(USER_INFO_0, USER_INFO_1)) + setUserTracker(0) underTest = CurrentTilesInteractorImpl( tileSpecRepository = tileSpecRepository, + installedTilesComponentRepository = installedTilesPackageRepository, userRepository = userRepository, customTileStatePersister = customTileStatePersister, tileFactory = tileFactory, @@ -609,6 +613,40 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { assertThat((tileA as FakeQSTile).callbacks).containsExactly(callback) } + @Test + fun packageNotInstalled_customTileNotVisible() = + testScope.runTest(USER_INFO_0) { + installedTilesPackageRepository.setInstalledPackagesForUser(USER_INFO_0.id, emptySet()) + + val tiles by collectLastValue(underTest.currentTiles) + + val specs = listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC) + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + + assertThat(tiles!!.size).isEqualTo(1) + assertThat(tiles!![0].spec).isEqualTo(specs[0]) + } + + @Test + fun packageInstalledLater_customTileAdded() = + testScope.runTest(USER_INFO_0) { + installedTilesPackageRepository.setInstalledPackagesForUser(USER_INFO_0.id, emptySet()) + + val tiles by collectLastValue(underTest.currentTiles) + val specs = listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC, TileSpec.create("b")) + tileSpecRepository.setTiles(USER_INFO_0.id, specs) + + assertThat(tiles!!.size).isEqualTo(2) + + installedTilesPackageRepository.setInstalledPackagesForUser( + USER_INFO_0.id, + setOf(TEST_COMPONENT) + ) + + assertThat(tiles!!.size).isEqualTo(3) + assertThat(tiles!![1].spec).isEqualTo(CUSTOM_TILE_SPEC) + } + private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) { this.state = state this.label = label @@ -654,6 +692,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private suspend fun switchUser(user: UserInfo) { setUserTracker(user.id) + installedTilesPackageRepository.setInstalledPackagesForUser(user.id, setOf(TEST_COMPONENT)) userRepository.setSelectedUserInfo(user) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 3def6ba1fc58..8744aa346f8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -204,6 +204,17 @@ public class RecordingServiceTest extends SysuiTestCase { } @Test + public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_showsErrorNotification() + throws IOException { + doReturn(true).when(mController).isRecording(); + doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(); + + mRecordingService.onStopped(); + + verify(mRecordingService).createErrorNotification(); + } + + @Test public void testOnSystemRequestedStop_recorderEndThrowsOOMError_releasesRecording() throws IOException { doReturn(true).when(mController).isRecording(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt new file mode 100644 index 000000000000..fbb77cdc3049 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot + +import android.app.Notification +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.drawable.Icon +import android.net.Uri +import android.os.UserHandle +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData +import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import java.util.concurrent.CompletableFuture +import java.util.function.Supplier +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test + +@SmallTest +class SaveImageInBackgroundTaskTest : SysuiTestCase() { + private val imageExporter = mock<ImageExporter>() + private val smartActions = mock<ScreenshotSmartActions>() + private val smartActionsProvider = mock<ScreenshotNotificationSmartActionsProvider>() + private val saveImageData = SaveImageInBackgroundData() + private val sharedTransitionSupplier = + mock<Supplier<ScreenshotController.SavedImageData.ActionTransition>>() + private val testScreenshotId: String = "testScreenshotId" + private val testBitmap = mock<Bitmap>() + private val testUser = UserHandle.getUserHandleForUid(0) + private val testIcon = mock<Icon>() + private val testImageTime = 1234.toLong() + private val flags = FakeFeatureFlags() + + private val smartActionsUriFuture = mock<CompletableFuture<List<Notification.Action>>>() + private val smartActionsFuture = mock<CompletableFuture<List<Notification.Action>>>() + + private val testUri: Uri = Uri.parse("testUri") + private val intent = + Intent(Intent.ACTION_SEND) + .setComponent( + ComponentName.unflattenFromString( + "com.google.android.test/com.google.android.test.TestActivity" + ) + ) + private val immutablePendingIntent = + PendingIntent.getBroadcast( + mContext, + 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + private val mutablePendingIntent = + PendingIntent.getBroadcast( + mContext, + 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE + ) + + private val saveImageTask = + SaveImageInBackgroundTask( + mContext, + flags, + imageExporter, + smartActions, + saveImageData, + sharedTransitionSupplier, + smartActionsProvider, + ) + + @Before + fun setup() { + whenever( + smartActions.getSmartActionsFuture( + eq(testScreenshotId), + any(Uri::class.java), + eq(testBitmap), + eq(smartActionsProvider), + any(ScreenshotSmartActionType::class.java), + any(Boolean::class.java), + eq(testUser) + ) + ) + .thenReturn(smartActionsUriFuture) + whenever( + smartActions.getSmartActionsFuture( + eq(testScreenshotId), + eq(null), + eq(testBitmap), + eq(smartActionsProvider), + any(ScreenshotSmartActionType::class.java), + any(Boolean::class.java), + eq(testUser) + ) + ) + .thenReturn(smartActionsFuture) + } + + @Test + fun testQueryQuickShare_noAction() { + whenever( + smartActions.getSmartActions( + eq(testScreenshotId), + eq(smartActionsFuture), + any(Int::class.java), + eq(smartActionsProvider), + eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) + ) + ) + .thenReturn(ArrayList<Notification.Action>()) + + val quickShareAction = + saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri) + + assertNull(quickShareAction) + } + + @Test + fun testQueryQuickShare_withActions() { + val actions = ArrayList<Notification.Action>() + actions.add(constructAction("Action One", mutablePendingIntent)) + actions.add(constructAction("Action Two", mutablePendingIntent)) + whenever( + smartActions.getSmartActions( + eq(testScreenshotId), + eq(smartActionsUriFuture), + any(Int::class.java), + eq(smartActionsProvider), + eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) + ) + ) + .thenReturn(actions) + + val quickShareAction = + saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)!! + + assertEquals("Action One", quickShareAction.title) + assertEquals(mutablePendingIntent, quickShareAction.actionIntent) + } + + @Test + fun testCreateQuickShareAction_originalWasNull_returnsNull() { + val quickShareAction = + saveImageTask.createQuickShareAction( + null, + testScreenshotId, + testUri, + testImageTime, + testBitmap, + testUser + ) + + assertNull(quickShareAction) + } + + @Test + fun testCreateQuickShareAction_immutableIntentDifferentAction_returnsNull() { + val actions = ArrayList<Notification.Action>() + actions.add(constructAction("New Test Action", immutablePendingIntent)) + whenever( + smartActions.getSmartActions( + eq(testScreenshotId), + eq(smartActionsUriFuture), + any(Int::class.java), + eq(smartActionsProvider), + eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) + ) + ) + .thenReturn(actions) + val origAction = constructAction("Old Test Action", immutablePendingIntent) + + val quickShareAction = + saveImageTask.createQuickShareAction( + origAction, + testScreenshotId, + testUri, + testImageTime, + testBitmap, + testUser, + ) + + assertNull(quickShareAction) + } + + @Test + fun testCreateQuickShareAction_mutableIntent_returnsSafeIntent() { + val actions = ArrayList<Notification.Action>() + val action = constructAction("Action One", mutablePendingIntent) + actions.add(action) + whenever( + smartActions.getSmartActions( + eq(testScreenshotId), + eq(smartActionsUriFuture), + any(Int::class.java), + eq(smartActionsProvider), + eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) + ) + ) + .thenReturn(actions) + + val quickShareAction = + saveImageTask.createQuickShareAction( + constructAction("Test Action", mutablePendingIntent), + testScreenshotId, + testUri, + testImageTime, + testBitmap, + testUser + ) + val quickSharePendingIntent = + quickShareAction.actionIntent.intent.extras!!.getParcelable( + ScreenshotController.EXTRA_ACTION_INTENT, + PendingIntent::class.java + ) + + assertEquals("Test Action", quickShareAction.title) + assertEquals(mutablePendingIntent, quickSharePendingIntent) + } + + @Test + fun testCreateQuickShareAction_immutableIntent_returnsSafeIntent() { + val actions = ArrayList<Notification.Action>() + val action = constructAction("Test Action", immutablePendingIntent) + actions.add(action) + whenever( + smartActions.getSmartActions( + eq(testScreenshotId), + eq(smartActionsUriFuture), + any(Int::class.java), + eq(smartActionsProvider), + eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) + ) + ) + .thenReturn(actions) + + val quickShareAction = + saveImageTask.createQuickShareAction( + constructAction("Test Action", immutablePendingIntent), + testScreenshotId, + testUri, + testImageTime, + testBitmap, + testUser, + )!! + + assertEquals("Test Action", quickShareAction.title) + assertEquals( + immutablePendingIntent, + quickShareAction.actionIntent.intent.extras!!.getParcelable( + ScreenshotController.EXTRA_ACTION_INTENT, + PendingIntent::class.java + ) + ) + } + + private fun constructAction(title: String, intent: PendingIntent): Notification.Action { + return Notification.Action.Builder(testIcon, title, intent).build() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index a5a9de54c558..8f2ee91d6a6a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -1097,6 +1097,13 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo } @Test + public void onShadeFlingEnd_mExpandImmediateShouldBeReset() { + mNotificationPanelViewController.onFlingEnd(false); + + verify(mQsController).setExpandImmediate(false); + } + + @Test public void inUnlockedSplitShade_transitioningMaxTransitionDistance_makesShadeFullyExpanded() { mStatusBarStateController.setState(SHADE); enableSplitShade(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index 20da8a619100..2da2e9238d0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -78,6 +78,7 @@ import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.reset +import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit @@ -387,7 +388,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever(clock.isLayoutRtl).thenReturn(false) val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) - verify(clock).addOnLayoutChangeListener(capture(captor)) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) captor.value.onLayoutChange(clock, 0, 1, 2, 3, 4, 5, 6, 7) verify(clock).pivotX = 0f @@ -400,7 +401,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { whenever(clock.isLayoutRtl).thenReturn(true) val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) - verify(clock).addOnLayoutChangeListener(capture(captor)) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) captor.value.onLayoutChange(clock, 0, 1, 2, 3, 4, 5, 6, 7) verify(clock).pivotX = width.toFloat() @@ -793,7 +794,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { @Test fun clockPivotYInCenter() { val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) - verify(clock).addOnLayoutChangeListener(capture(captor)) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) var height = 100 val width = 50 @@ -825,16 +826,17 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @Test - fun carrierLeftPaddingIsSetWhenClockLayoutChanges() { - val width = 200 - whenever(clock.width).thenReturn(width) - whenever(clock.scaleX).thenReturn(2.57f) // 2.57 comes from qs_header.xml - val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) + fun carrierStartPaddingIsSetOnClockLayout() { + val clockWidth = 200 + val maxClockScale = context.resources.getFloat(R.dimen.qqs_expand_clock_scale) + val expectedStartPadding = (clockWidth * maxClockScale).toInt() + whenever(clock.width).thenReturn(clockWidth) - verify(clock).addOnLayoutChangeListener(capture(captor)) - captor.value.onLayoutChange(clock, 0, 0, width, 0, 0, 0, 0, 0) + val captor = ArgumentCaptor.forClass(View.OnLayoutChangeListener::class.java) + verify(clock, times(2)).addOnLayoutChangeListener(capture(captor)) + captor.allValues.forEach { clock.executeLayoutChange(0, 0, clockWidth, 0, it) } - verify(carrierGroup).setPaddingRelative(514, 0, 0, 0) + verify(carrierGroup).setPaddingRelative(expectedStartPadding, 0, 0, 0) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index eef4470c48cd..04c93cb71e42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -298,6 +298,54 @@ class ClockRegistryTest : SysuiTestCase() { } @Test + fun unknownPluginAttached_clockAndListUnchanged_loadRequested() { + val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>() + whenever(mockPluginLifecycle.getPackage()).thenReturn("some.other.package") + + var changeCallCount = 0 + var listChangeCallCount = 0 + registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener { + override fun onCurrentClockChanged() { changeCallCount++ } + override fun onAvailableClocksChanged() { listChangeCallCount++ } + }) + + assertEquals(true, pluginListener.onPluginAttached(mockPluginLifecycle)) + scheduler.runCurrent() + assertEquals(0, changeCallCount) + assertEquals(0, listChangeCallCount) + } + + @Test + fun knownPluginAttached_clockAndListChanged_notLoaded() { + val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>() + whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.falcon.one") + val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>() + whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.falcon.two") + + var changeCallCount = 0 + var listChangeCallCount = 0 + registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener { + override fun onCurrentClockChanged() { changeCallCount++ } + override fun onAvailableClocksChanged() { listChangeCallCount++ } + }) + + registry.applySettings(ClockSettings("DIGITAL_CLOCK_CALLIGRAPHY", null)) + scheduler.runCurrent() + assertEquals(1, changeCallCount) + assertEquals(0, listChangeCallCount) + + assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle1)) + scheduler.runCurrent() + assertEquals(1, changeCallCount) + assertEquals(1, listChangeCallCount) + + assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle2)) + scheduler.runCurrent() + assertEquals(1, changeCallCount) + assertEquals(2, listChangeCallCount) + } + + @Test fun pluginAddRemove_concurrentModification() { val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>() val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java index d5e904c636d5..88d853e244e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -122,6 +122,7 @@ public class PluginInstanceTest extends SysuiTestCase { mPluginInstanceFactory.create( mContext, mAppInfo, wrongVersionTestPluginComponentName, TestPlugin.class, mPluginListener); + mPluginInstance.onCreate(); } @Test @@ -135,11 +136,12 @@ public class PluginInstanceTest extends SysuiTestCase { @Test public void testOnDestroy() { + mPluginInstance.onCreate(); mPluginInstance.onDestroy(); assertEquals(1, mPluginListener.mDetachedCount); assertEquals(1, mPluginListener.mUnloadCount); assertNull(mPluginInstance.getPlugin()); - assertInstances(0, -1); // Destroyed but never created + assertInstances(0, 0); // Destroyed but never created } @Test @@ -161,6 +163,16 @@ public class PluginInstanceTest extends SysuiTestCase { assertInstances(0, 0); } + @Test + public void testOnAttach_SkipLoad() { + mPluginListener.mAttachReturn = false; + mPluginInstance.onCreate(); + assertEquals(1, mPluginListener.mAttachedCount); + assertEquals(0, mPluginListener.mLoadCount); + assertEquals(null, mPluginInstance.getPlugin()); + assertInstances(0, 0); + } + // This target class doesn't matter, it just needs to have a Requires to hit the flow where // the mock version info is called. @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION) @@ -220,15 +232,17 @@ public class PluginInstanceTest extends SysuiTestCase { } public class FakeListener implements PluginListener<TestPlugin> { + public boolean mAttachReturn = true; public int mAttachedCount = 0; public int mDetachedCount = 0; public int mLoadCount = 0; public int mUnloadCount = 0; @Override - public void onPluginAttached(PluginLifecycleManager<TestPlugin> manager) { + public boolean onPluginAttached(PluginLifecycleManager<TestPlugin> manager) { mAttachedCount++; assertEquals(PluginInstanceTest.this.mPluginInstance, manager); + return mAttachReturn; } @Override 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 39ed5535ff3b..914301f2e830 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 @@ -60,9 +60,13 @@ import org.mockito.MockitoAnnotations class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { @Mock private lateinit var systemEventCoordinator: SystemEventCoordinator + @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var statusBarContentInsetProvider: StatusBarContentInsetsProvider + @Mock private lateinit var dumpManager: DumpManager + @Mock private lateinit var listener: SystemStatusAnimationCallback private lateinit var systemClock: FakeSystemClock @@ -380,6 +384,32 @@ class SystemStatusAnimationSchedulerImplTest : SysuiTestCase() { } @Test + fun testPrivacyDot_isRemovedDuringChipDisappearAnimation() = runTest { + // Instantiate class under test with TestScope from runTest + initializeSystemStatusAnimationScheduler(testScope = this) + + // create and schedule high priority event + createAndScheduleFakePrivacyEvent() + + // fast forward to ANIMATING_OUT state + fastForwardAnimationToState(ANIMATING_OUT) + assertEquals(ANIMATING_OUT, systemStatusAnimationScheduler.getAnimationState()) + verify(listener, times(1)).onSystemStatusAnimationTransitionToPersistentDot(any()) + + // remove persistent dot + systemStatusAnimationScheduler.removePersistentDot() + testScheduler.runCurrent() + + // skip disappear animation + animatorTestRule.advanceTimeBy(DISAPPEAR_ANIMATION_DURATION) + testScheduler.runCurrent() + + // verify that animationState changes to IDLE and onHidePersistentDot callback is invoked + assertEquals(IDLE, systemStatusAnimationScheduler.getAnimationState()) + verify(listener, times(1)).onHidePersistentDot() + } + + @Test fun testPrivacyEvent_forceVisibleIsUpdated_whenRescheduledDuringQueuedState() = runTest { // Instantiate class under test with TestScope from runTest initializeSystemStatusAnimationScheduler(testScope = this) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 283efe263f04..257cc5b1b85c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -221,16 +221,35 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { } @Test - fun hunExtensionCancelledWhenHunActionPressed() { + fun actionPressCancelsExistingLifetimeExtension() { whenever(headsUpManager.isSticky(anyString())).thenReturn(true) addHUN(entry) + whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false) whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L) - assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0)) + assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason = */ 0)) + actionPressListener.accept(entry) - executor.advanceClockToLast() executor.runAllReady() - verify(headsUpManager, times(1)).removeNotification(eq(entry.key), eq(true)) + verify(endLifetimeExtension, times(1)).onEndLifetimeExtension(notifLifetimeExtender, entry) + + collectionListener.onEntryRemoved(entry, /* reason = */ 0) + verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any()) + } + + @Test + fun actionPressPreventsFutureLifetimeExtension() { + whenever(headsUpManager.isSticky(anyString())).thenReturn(true) + addHUN(entry) + + actionPressListener.accept(entry) + verify(headsUpManager, times(1)).setUserActionMayIndirectlyRemove(entry) + + whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(true) + assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0)) + + collectionListener.onEntryRemoved(entry, /* reason = */ 0) + verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any()) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 6a0e3c6d51eb..02666e40b98d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.content.res.Resources; import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; +import android.view.View; import androidx.test.filters.SmallTest; @@ -430,6 +431,84 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { verify(mNotificationStackScrollLayout).setStatusBarState(KEYGUARD); } + @Test + public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() { + // GIVEN: Controller is attached, active notifications is empty, + // and mNotificationStackScrollLayout.onKeyguard() is true + initController(/* viewIsAttached= */ true); + when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true); + mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); + + // WHEN: call updateImportantForAccessibility + mController.updateImportantForAccessibility(); + + // THEN: mNotificationStackScrollLayout should not be important for A11y + verify(mNotificationStackScrollLayout) + .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } + + @Test + public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() { + // GIVEN: Controller is attached, active notifications is not empty, + // and mNotificationStackScrollLayout.onKeyguard() is true + initController(/* viewIsAttached= */ true); + when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true); + mController.getNotifStackController().setNotifStats( + new NotifStats( + /* numActiveNotifs = */ 1, + /* hasNonClearableAlertingNotifs = */ false, + /* hasClearableAlertingNotifs = */ false, + /* hasNonClearableSilentNotifs = */ false, + /* hasClearableSilentNotifs = */ false) + ); + + // WHEN: call updateImportantForAccessibility + mController.updateImportantForAccessibility(); + + // THEN: mNotificationStackScrollLayout should be important for A11y + verify(mNotificationStackScrollLayout) + .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + + @Test + public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() { + // GIVEN: Controller is attached, active notifications is not empty, + // and mNotificationStackScrollLayout.onKeyguard() is false + initController(/* viewIsAttached= */ true); + when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false); + mController.getNotifStackController().setNotifStats( + new NotifStats( + /* numActiveNotifs = */ 1, + /* hasNonClearableAlertingNotifs = */ false, + /* hasClearableAlertingNotifs = */ false, + /* hasNonClearableSilentNotifs = */ false, + /* hasClearableSilentNotifs = */ false) + ); + + // WHEN: call updateImportantForAccessibility + mController.updateImportantForAccessibility(); + + // THEN: mNotificationStackScrollLayout should be important for A11y + verify(mNotificationStackScrollLayout) + .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + + @Test + public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() { + // GIVEN: Controller is attached, active notifications is empty, + // and mNotificationStackScrollLayout.onKeyguard() is false + initController(/* viewIsAttached= */ true); + when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false); + mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); + + // WHEN: call updateImportantForAccessibility + mController.updateImportantForAccessibility(); + + // THEN: mNotificationStackScrollLayout should be important for A11y + verify(mNotificationStackScrollLayout) + .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + private LogMaker logMatcher(int category, int type) { return argThat(new LogMatcher(category, type)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index a4ee349f5b71..85b1ec108e98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -575,10 +576,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.inflateFooterView(); // add notification - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); - NotificationEntry entry = mock(NotificationEntry.class); - when(row.getEntry()).thenReturn(entry); - when(entry.isClearable()).thenReturn(true); + ExpandableNotificationRow row = createClearableRow(); mStackScroller.addContainerView(row); mStackScroller.onUpdateRowStates(); @@ -648,6 +646,50 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + public void testClearNotifications_clearAllInProgress() { + ExpandableNotificationRow row = createClearableRow(); + when(row.getEntry().hasFinishedInitialization()).thenReturn(true); + doReturn(true).when(mStackScroller).isVisible(row); + mStackScroller.addContainerView(row); + + mStackScroller.clearNotifications(ROWS_ALL, false); + + assertClearAllInProgress(true); + verify(mNotificationRoundnessManager).setClearAllInProgress(true); + } + + @Test + public void testOnChildAnimationFinished_resetsClearAllInProgress() { + mStackScroller.setClearAllInProgress(true); + + mStackScroller.onChildAnimationFinished(); + + assertClearAllInProgress(false); + verify(mNotificationRoundnessManager).setClearAllInProgress(false); + } + + @Test + public void testShadeCollapsed_resetsClearAllInProgress() { + mStackScroller.setClearAllInProgress(true); + + mStackScroller.setIsExpanded(false); + + assertClearAllInProgress(false); + verify(mNotificationRoundnessManager).setClearAllInProgress(false); + } + + @Test + public void testShadeExpanded_doesntChangeClearAllInProgress() { + mStackScroller.setClearAllInProgress(true); + clearInvocations(mNotificationRoundnessManager); + + mStackScroller.setIsExpanded(true); + + assertClearAllInProgress(true); + verify(mNotificationRoundnessManager, never()).setClearAllInProgress(anyBoolean()); + } + + @Test public void testAddNotificationUpdatesSpeedBumpIndex() { // initial state calculated == 0 assertEquals(0, mStackScroller.getSpeedBumpIndex()); @@ -794,7 +836,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void onShadeClosesWithAnimationWillResetSwipeState() { + public void onShadeClosesWithAnimationWillResetTouchState() { // GIVEN shade is expanded mStackScroller.setIsExpanded(true); clearInvocations(mNotificationSwipeHelper); @@ -804,12 +846,12 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.setIsExpanded(false); mStackScroller.onExpansionStopped(); - // VERIFY swipe is reset - verify(mNotificationSwipeHelper).resetSwipeState(); + // VERIFY touch is reset + verify(mNotificationSwipeHelper).resetTouchState(); } @Test - public void onShadeClosesWithoutAnimationWillResetSwipeState() { + public void onShadeClosesWithoutAnimationWillResetTouchState() { // GIVEN shade is expanded mStackScroller.setIsExpanded(true); clearInvocations(mNotificationSwipeHelper); @@ -817,8 +859,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // WHEN closing the shade without the animation mStackScroller.setIsExpanded(false); - // VERIFY swipe is reset - verify(mNotificationSwipeHelper).resetSwipeState(); + // VERIFY touch is reset + verify(mNotificationSwipeHelper).resetTouchState(); } @Test @@ -896,6 +938,21 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScroller.setStatusBarState(state); } + private ExpandableNotificationRow createClearableRow() { + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); + NotificationEntry entry = mock(NotificationEntry.class); + when(row.canViewBeCleared()).thenReturn(true); + when(row.getEntry()).thenReturn(entry); + when(entry.isClearable()).thenReturn(true); + + return row; + } + + private void assertClearAllInProgress(boolean expected) { + assertEquals(expected, mStackScroller.getClearAllInProgress()); + assertEquals(expected, mAmbientState.isClearAllInProgress()); + } + private static void mockBoundsOnScreen(View view, Rect bounds) { doAnswer(invocation -> { Rect out = invocation.getArgument(0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 7632d01d4d43..df65c09eb8a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -21,6 +21,7 @@ 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.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -29,6 +30,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.animation.Animator; @@ -670,6 +672,28 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { } @Test + public void testForceResetSwipeStateDoesNothingIfTranslationIsZero() { + doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth(); + doReturn(0f).when(mNotificationRow).getTranslationX(); + + mSwipeHelper.forceResetSwipeState(mNotificationRow); + + verify(mNotificationRow).getTranslationX(); + verifyNoMoreInteractions(mNotificationRow); + } + + @Test + public void testForceResetSwipeStateResetsTranslationAndAlpha() { + doReturn(FAKE_ROW_WIDTH).when(mNotificationRow).getMeasuredWidth(); + doReturn(10f).when(mNotificationRow).getTranslationX(); + + mSwipeHelper.forceResetSwipeState(mNotificationRow); + + verify(mNotificationRow).setTranslation(eq(0f)); + verify(mNotificationRow).setContentAlpha(eq(1f)); + } + + @Test public void testContentAlphaRemainsUnchangedWhenFeatureFlagIsDisabled() { // Returning true prevents alpha fade. In an unmocked scenario the callback is instantiated diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index e12d179c5aa5..88b1eb3d565c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -35,7 +35,6 @@ import org.mockito.Mockito.`when` as whenever @SmallTest class StackScrollAlgorithmTest : SysuiTestCase() { - @JvmField @Rule var expect: Expect = Expect.create() @@ -82,26 +81,58 @@ class StackScrollAlgorithmTest : SysuiTestCase() { fun resetViewStates_defaultHun_yTranslationIsInset() { whenever(notificationRow.isPinned).thenReturn(true) whenever(notificationRow.isHeadsUp).thenReturn(true) - - stackScrollAlgorithm.resetViewStates(ambientState, 0) - - assertThat(notificationRow.viewState.yTranslation) - .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) + resetViewStates_hunYTranslationIsInset() } @Test - fun resetViewStates_stackMargin_changesHunYTranslation() { + fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() { whenever(notificationRow.isPinned).thenReturn(true) whenever(notificationRow.isHeadsUp).thenReturn(true) - val minHeadsUpTranslation = context.resources - .getDimensionPixelSize(R.dimen.notification_side_paddings) + resetViewStates_stackMargin_changesHunYTranslation() + } - // split shade case with top margin introduced by shade's status bar - ambientState.stackTopMargin = 100 - stackScrollAlgorithm.resetViewStates(ambientState, 0) + @Test + fun resetViewStates_hunAnimatingAway_yTranslationIsInset() { + whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) + resetViewStates_hunYTranslationIsInset() + } - // top margin presence should decrease heads up translation up to minHeadsUpTranslation - assertThat(notificationRow.viewState.yTranslation).isEqualTo(minHeadsUpTranslation) + @Test + fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() { + whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) + resetViewStates_stackMargin_changesHunYTranslation() + } + + @Test + fun resetViewStates_hunAnimatingAway_bottomNotClipped() { + whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true) + + stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) + + assertThat(notificationRow.viewState.clipBottomAmount).isEqualTo(0) + } + + @Test + fun resetViewStates_hunsOverlapping_bottomHunClipped() { + val topHun = mockExpandableNotificationRow() + val bottomHun = mockExpandableNotificationRow() + whenever(topHun.isHeadsUp).thenReturn(true) + whenever(topHun.isPinned).thenReturn(true) + whenever(bottomHun.isHeadsUp).thenReturn(true) + whenever(bottomHun.isPinned).thenReturn(true) + + resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun) + } + + @Test + fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() { + val topHun = mockExpandableNotificationRow() + val bottomHun = mockExpandableNotificationRow() + whenever(topHun.isHeadsUp).thenReturn(true) + whenever(topHun.isPinned).thenReturn(true) + whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true) + + resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun) } @Test @@ -855,6 +886,57 @@ class StackScrollAlgorithmTest : SysuiTestCase() { ambientState.stackHeight = ambientState.stackEndHeight * fraction } + private fun resetViewStates_hunYTranslationIsInset() { + stackScrollAlgorithm.resetViewStates(ambientState, 0) + + assertThat(notificationRow.viewState.yTranslation) + .isEqualTo(stackScrollAlgorithm.mHeadsUpInset) + } + + private fun resetViewStates_stackMargin_changesHunYTranslation() { + val stackTopMargin = 50 + val headsUpTranslationY = stackScrollAlgorithm.mHeadsUpInset - stackTopMargin + + // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild + ambientState.shelf = notificationShelf + + // split shade case with top margin introduced by shade's status bar + ambientState.stackTopMargin = stackTopMargin + stackScrollAlgorithm.resetViewStates(ambientState, 0) + + // heads up translation should be decreased by the top margin + assertThat(notificationRow.viewState.yTranslation).isEqualTo(headsUpTranslationY) + } + + private fun resetViewStates_hunsOverlapping_bottomHunClipped( + topHun: ExpandableNotificationRow, + bottomHun: ExpandableNotificationRow + ) { + val topHunHeight = mContext.resources.getDimensionPixelSize( + R.dimen.notification_content_min_height) + val bottomHunHeight = mContext.resources.getDimensionPixelSize( + R.dimen.notification_max_heads_up_height) + whenever(topHun.intrinsicHeight).thenReturn(topHunHeight) + whenever(bottomHun.intrinsicHeight).thenReturn(bottomHunHeight) + + // we need the shelf to mock the real-life behaviour of StackScrollAlgorithm#updateChild + ambientState.shelf = notificationShelf + + // add two overlapping HUNs + hostView.removeAllViews() + hostView.addView(topHun) + hostView.addView(bottomHun) + + stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0) + + // the height shouldn't change + assertThat(topHun.viewState.height).isEqualTo(topHunHeight) + assertThat(bottomHun.viewState.height).isEqualTo(bottomHunHeight) + // the HUN at the bottom should be clipped + assertThat(topHun.viewState.clipBottomAmount).isEqualTo(0) + assertThat(bottomHun.viewState.clipBottomAmount).isEqualTo(bottomHunHeight - topHunHeight) + } + private fun resetViewStates_expansionChanging_notificationAlphaUpdated( expansionFraction: Float, expectedAlpha: Float, 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 fd9f6a73aee4..8062272a0042 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 @@ -350,6 +350,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { // For the Shade to animate during the Back gesture, we must enable the animation flag. mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true); mFeatureFlags.set(Flags.LIGHT_REVEAL_MIGRATION, true); + mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, @@ -1103,8 +1104,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { clearInvocations(mNotificationPanelViewController); mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp(); - verify(mDozeServiceHost).stopDozing(); + verify(mDozeServiceHost, never()).stopDozing(); verify(mNotificationPanelViewController).expand(eq(false)); + mCentralSurfaces.mWakefulnessObserver.onFinishedWakingUp(); + verify(mDozeServiceHost).stopDozing(); } @Test @@ -1118,6 +1121,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.setBouncerShowing(true); mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp(); verify(mNotificationPanelViewController, never()).expand(anyBoolean()); + mCentralSurfaces.mWakefulnessObserver.onFinishedWakingUp(); + verify(mDozeServiceHost).stopDozing(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 7e69efaaa640..5d2b59b6e00e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -67,6 +67,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { private float mQsExpansion; private int mCutoutTopInset = 0; private boolean mIsSplitShade = false; + private boolean mBypassEnabled = false; + private int mUnlockedStackScrollerPadding = 0; private float mUdfpsTop = -1; private float mClockBottom = SCREEN_HEIGHT / 2; private boolean mClockTopAligned; @@ -339,15 +341,52 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { } @Test - public void notifMinPaddingAlignedWithClockInSplitShadeMode() { + public void notifPadding_splitShade() { givenLockScreen(); mIsSplitShade = true; mKeyguardStatusHeight = 200; // WHEN the position algorithm is run positionClock(); // THEN the padding DOESN'T adjust for keyguard status height. - assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()) - .isEqualTo(mKeyguardStatusBarHeaderHeight); + assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 10)) + .isEqualTo(mKeyguardStatusBarHeaderHeight - 10); + } + + @Test + public void notifPadding_portraitShade_bypassOff() { + givenLockScreen(); + mIsSplitShade = false; + mBypassEnabled = false; + + // mMinTopMargin = 100 = 80 + max(20, 0) + mKeyguardStatusBarHeaderHeight = 80; + mUserSwitchHeight = 20; + when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)) + .thenReturn(0); + + mKeyguardStatusHeight = 200; + + // WHEN the position algorithm is run + positionClock(); + + // THEN padding = 300 = mMinTopMargin(100) + mKeyguardStatusHeight(200) + assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50)) + .isEqualTo(300); + } + + @Test + public void notifPadding_portraitShade_bypassOn() { + givenLockScreen(); + mIsSplitShade = false; + mBypassEnabled = true; + mUnlockedStackScrollerPadding = 200; + + // WHEN the position algorithm is run + positionClock(); + + // THEN padding = 150 = mUnlockedStackScrollerPadding(200) - nsslTop(50) + assertThat(mClockPositionAlgorithm.getLockscreenNotifPadding(/* nsslTop= */ 50)) + .isEqualTo(150); } @Test @@ -589,8 +628,8 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { 0 /* userSwitchPreferredY */, mDark, ZERO_DRAG, - false /* bypassEnabled */, - 0 /* unlockedStackScrollerPadding */, + mBypassEnabled, + mUnlockedStackScrollerPadding, mQsExpansion, mCutoutTopInset, mIsSplitShade, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index f054422e6524..c4e419366759 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt @@ -40,6 +40,8 @@ class FakeMobileIconInteractor( ) ) + override val carrierNetworkChangeActive = MutableStateFlow(false) + override val mobileIsDefault = MutableStateFlow(true) override val networkTypeIconGroup = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt new file mode 100644 index 000000000000..01c388a523e6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.ui.model + +import androidx.test.filters.SmallTest +import com.android.settingslib.graph.SignalDrawable +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameters + +@SmallTest +@RunWith(Parameterized::class) +internal class SignalIconModelParameterizedTest(private val testCase: TestCase) : SysuiTestCase() { + @Test + fun drawableFromModel_level0_numLevels4_noExclamation_notCarrierNetworkChange() { + val model = + SignalIconModel( + level = 0, + numberOfLevels = 4, + showExclamationMark = false, + carrierNetworkChange = false + ) + + val expected = + SignalDrawable.getState(/* level = */ 0, /* numLevels = */ 4, /* cutOut = */ false) + + assertThat(model.toSignalDrawableState()).isEqualTo(expected) + } + + @Test + fun runTest() { + val model = testCase.toSignalIconModel() + assertThat(model.toSignalDrawableState()).isEqualTo(testCase.expected) + } + + internal data class TestCase( + val level: Int, + val numberOfLevels: Int, + val showExclamation: Boolean, + val carrierNetworkChange: Boolean, + val expected: Int, + ) { + fun toSignalIconModel() = + SignalIconModel( + level = level, + numberOfLevels = numberOfLevels, + showExclamationMark = showExclamation, + carrierNetworkChange = carrierNetworkChange, + ) + + override fun toString(): String = + "INPUT(level=$level," + + "numberOfLevels=$numberOfLevels," + + "showExclamation=$showExclamation," + + "carrierNetworkChange=$carrierNetworkChange)" + } + + companion object { + @Parameters(name = "{0}") @JvmStatic fun data() = testData() + + private fun testData(): Collection<TestCase> = + listOf( + TestCase( + level = 0, + numberOfLevels = 4, + showExclamation = false, + carrierNetworkChange = false, + expected = SignalDrawable.getState(0, 4, false) + ), + TestCase( + level = 0, + numberOfLevels = 4, + showExclamation = false, + carrierNetworkChange = true, + expected = SignalDrawable.getCarrierChangeState(4) + ), + TestCase( + level = 2, + numberOfLevels = 5, + showExclamation = false, + carrierNetworkChange = false, + expected = SignalDrawable.getState(2, 5, false) + ), + TestCase( + level = 2, + numberOfLevels = 5, + showExclamation = true, + carrierNetworkChange = false, + expected = SignalDrawable.getState(2, 5, true) + ), + TestCase( + level = 2, + numberOfLevels = 5, + showExclamation = true, + carrierNetworkChange = true, + expected = SignalDrawable.getCarrierChangeState(5) + ), + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index 2b7bc780f7c7..b5ab29d6217e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -273,6 +273,27 @@ class MobileIconViewModelTest : SysuiTestCase() { } @Test + fun icon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() = + testScope.runTest { + var latest: SignalIconModel? = null + val job = underTest.icon.onEach { latest = it }.launchIn(this) + + interactor.carrierNetworkChangeActive.value = true + interactor.level.value = 1 + + assertThat(latest!!.level).isEqualTo(1) + assertThat(latest!!.carrierNetworkChange).isTrue() + + // SignalIconModel respects the current level + interactor.level.value = 2 + + assertThat(latest!!.level).isEqualTo(2) + assertThat(latest!!.carrierNetworkChange).isTrue() + + job.cancel() + } + + @Test fun contentDescription_notInService_usesNoPhone() = testScope.runTest { var latest: ContentDescription? = null @@ -338,6 +359,20 @@ class MobileIconViewModelTest : SysuiTestCase() { } @Test + fun networkType_null_whenCarrierNetworkChangeActive() = + testScope.runTest { + interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G) + interactor.carrierNetworkChangeActive.value = true + interactor.mobileIsDefault.value = true + var latest: Icon? = null + val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + + assertThat(latest).isNull() + + job.cancel() + } + + @Test fun networkTypeIcon_notNull_whenEnabled() = testScope.runTest { val expected = @@ -617,13 +652,14 @@ class MobileIconViewModelTest : SysuiTestCase() { } private fun createAndSetViewModel() { - underTest = MobileIconViewModel( - SUB_1_ID, - interactor, - airplaneModeInteractor, - constants, - testScope.backgroundScope, - ) + underTest = + MobileIconViewModel( + SUB_1_ID, + interactor, + airplaneModeInteractor, + constants, + testScope.backgroundScope, + ) } companion object { @@ -632,10 +668,20 @@ class MobileIconViewModelTest : SysuiTestCase() { /** Convenience constructor for these tests */ fun defaultSignal(level: Int = 1): SignalIconModel { - return SignalIconModel(level, NUM_LEVELS, showExclamationMark = false) + return SignalIconModel( + level, + NUM_LEVELS, + showExclamationMark = false, + carrierNetworkChange = false, + ) } fun emptySignal(): SignalIconModel = - SignalIconModel(level = 0, numberOfLevels = NUM_LEVELS, showExclamationMark = true) + SignalIconModel( + level = 0, + numberOfLevels = NUM_LEVELS, + showExclamationMark = true, + carrierNetworkChange = false, + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java index 487d26d21824..a797e032a353 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java @@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -346,4 +345,17 @@ public class HeadsUpManagerTest extends AlertingNotificationManagerTest { assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(), mUiEventLoggerFake.eventId(0)); } + + @Test + public void testSetUserActionMayIndirectlyRemove() { + NotificationEntry notifEntry = new NotificationEntryBuilder() + .setSbn(createNewNotification(/* id= */ 0)) + .build(); + + mHeadsUpManager.showNotification(notifEntry); + assertFalse(mHeadsUpManager.canRemoveImmediately(notifEntry.getKey())); + + mHeadsUpManager.setUserActionMayIndirectlyRemove(notifEntry); + assertTrue(mHeadsUpManager.canRemoveImmediately(notifEntry.getKey())); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt index 4a28cd1de255..56c624565971 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt @@ -4,7 +4,7 @@ import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionPr class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener { - private val listeners = arrayListOf<TransitionProgressListener>() + private val listeners = mutableListOf<TransitionProgressListener>() override fun destroy() { listeners.clear() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt index d3fdbd94a5ac..b9c7e6133669 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt @@ -15,15 +15,15 @@ */ package com.android.systemui.unfold +import android.os.VibrationAttributes import android.os.VibrationEffect import android.os.Vibrator import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.util.TestFoldProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock -import java.util.concurrent.Executor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -54,7 +54,7 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() { progressProvider.onTransitionProgress(0.5f) progressProvider.onTransitionFinishing() - verify(vibrator).vibrate(any<VibrationEffect>()) + verify(vibrator).vibrate(any<VibrationEffect>(), any<VibrationAttributes>()) } @Test @@ -65,7 +65,7 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() { progressProvider.onTransitionProgress(0.99f) progressProvider.onTransitionFinishing() - verify(vibrator, never()).vibrate(any<VibrationEffect>()) + verify(vibrator, never()).vibrate(any<VibrationEffect>(), any<VibrationAttributes>()) } @Test @@ -85,7 +85,7 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() { progressProvider.onTransitionFinished() testFoldProvider.onFoldUpdate(isFolded = true) - verify(vibrator, never()).vibrate(any<VibrationEffect>()) + verify(vibrator, never()).vibrate(any<VibrationEffect>(), any<VibrationAttributes>()) } @Test @@ -113,22 +113,6 @@ class UnfoldHapticsPlayerTest : SysuiTestCase() { progressProvider.onTransitionFinishing() progressProvider.onTransitionFinished() - verify(vibrator).vibrate(any<VibrationEffect>()) - } - - private class TestFoldProvider : FoldProvider { - private val listeners = arrayListOf<FoldProvider.FoldCallback>() - - override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) { - listeners += callback - } - - override fun unregisterCallback(callback: FoldProvider.FoldCallback) { - listeners -= callback - } - - fun onFoldUpdate(isFolded: Boolean) { - listeners.forEach { it.onFoldUpdated(isFolded) } - } + verify(vibrator).vibrate(any<VibrationEffect>(), any<VibrationAttributes>()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt index e2aef31b4f10..e461e3f7fb1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt @@ -74,6 +74,11 @@ class TestUnfoldProgressListener : UnfoldTransitionProgressProvider.TransitionPr currentRecording?.assertLastProgress(progress) ?: error("unfold not in progress.") } + fun clear() { + currentRecording = null + recordings.clear() + } + class UnfoldTransitionRecording { private val progressHistory: MutableList<Float> = arrayListOf() private var finishingInvocations: Int = 0 diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt new file mode 100644 index 000000000000..35df35ccfe9c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.android.systemui.unfold.util + +import com.android.systemui.unfold.updates.FoldProvider +import java.util.concurrent.Executor + +class TestFoldProvider : FoldProvider { + private val listeners = arrayListOf<FoldProvider.FoldCallback>() + + override fun registerCallback(callback: FoldProvider.FoldCallback, executor: Executor) { + listeners += callback + } + + override fun unregisterCallback(callback: FoldProvider.FoldCallback) { + listeners -= callback + } + + fun onFoldUpdate(isFolded: Boolean) { + listeners.forEach { it.onFoldUpdated(isFolded) } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt new file mode 100644 index 000000000000..4a38fc069d9f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.unfold.util + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.progress.TestUnfoldProgressListener +import com.google.common.util.concurrent.MoreExecutors +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class UnfoldOnlyProgressProviderTest : SysuiTestCase() { + + private val listener = TestUnfoldProgressListener() + private val sourceProvider = TestUnfoldTransitionProvider() + + private val foldProvider = TestFoldProvider() + + private lateinit var progressProvider: UnfoldOnlyProgressProvider + + @Before + fun setUp() { + progressProvider = + UnfoldOnlyProgressProvider(foldProvider, MoreExecutors.directExecutor(), sourceProvider) + + progressProvider.addCallback(listener) + } + + @Test + fun unfolded_unfoldAnimationFinished_propagatesEvents() { + foldProvider.onFoldUpdate(isFolded = true) + foldProvider.onFoldUpdate(isFolded = false) + + // Unfold animation + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.5f) + sourceProvider.onTransitionFinished() + + with(listener.ensureTransitionFinished()) { + assertLastProgress(0.5f) + } + } + + @Test + fun unfoldedWithAnimation_foldAnimation_doesNotPropagateEvents() { + foldProvider.onFoldUpdate(isFolded = true) + foldProvider.onFoldUpdate(isFolded = false) + // Unfold animation + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.5f) + sourceProvider.onTransitionFinished() + listener.clear() + + // Fold animation + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.2f) + sourceProvider.onTransitionFinished() + + listener.assertNotStarted() + } + + @Test + fun unfoldedWithAnimation_foldAnimationSeveralTimes_doesNotPropagateEvents() { + foldProvider.onFoldUpdate(isFolded = true) + foldProvider.onFoldUpdate(isFolded = false) + // Unfold animation + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.5f) + sourceProvider.onTransitionFinished() + listener.clear() + + // Start and stop fold animation several times + repeat(3) { + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.2f) + sourceProvider.onTransitionFinished() + } + + listener.assertNotStarted() + } + + @Test + fun unfoldedAgainAfterFolding_propagatesEvents() { + foldProvider.onFoldUpdate(isFolded = true) + foldProvider.onFoldUpdate(isFolded = false) + + // Unfold animation + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.5f) + sourceProvider.onTransitionFinished() + + // Fold animation + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.2f) + sourceProvider.onTransitionFinished() + foldProvider.onFoldUpdate(isFolded = true) + + listener.clear() + + // Second unfold animation after folding + foldProvider.onFoldUpdate(isFolded = false) + sourceProvider.onTransitionStarted() + sourceProvider.onTransitionProgress(0.1f) + sourceProvider.onTransitionFinished() + + with(listener.ensureTransitionFinished()) { + assertLastProgress(0.1f) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt new file mode 100644 index 000000000000..2013bb0a547e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.kotlin + +import android.content.ComponentName +import android.content.pm.ComponentInfo +import android.content.pm.PackageManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameters +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(Parameterized::class) +internal class PackageManagerExtComponentEnabledTest(private val testCase: TestCase) : + SysuiTestCase() { + + @Mock private lateinit var packageManager: PackageManager + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testComponentActuallyEnabled() { + whenever(packageManager.getComponentEnabledSetting(TEST_COMPONENT)) + .thenReturn(testCase.componentEnabledSetting) + val componentInfo = + mock<ComponentInfo>() { + whenever(isEnabled).thenReturn(testCase.componentIsEnabled) + whenever(componentName).thenReturn(TEST_COMPONENT) + } + + assertThat(packageManager.isComponentActuallyEnabled(componentInfo)) + .isEqualTo(testCase.expected) + } + + internal data class TestCase( + @PackageManager.EnabledState val componentEnabledSetting: Int, + val componentIsEnabled: Boolean, + val expected: Boolean, + ) { + override fun toString(): String { + return "WHEN(" + + "componentIsEnabled = $componentIsEnabled, " + + "componentEnabledSetting = ${enabledStateToString()}) then " + + "EXPECTED = $expected" + } + + private fun enabledStateToString() = + when (componentEnabledSetting) { + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT -> "STATE_DEFAULT" + PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> "STATE_DISABLED" + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED -> { + "STATE_DISABLED_UNTIL_USED" + } + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> "STATE_DISABLED_USER" + PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> "STATE_ENABLED" + else -> "INVALID STATE" + } + } + + companion object { + @Parameters(name = "{0}") @JvmStatic fun data(): Collection<TestCase> = testData + + private val testDataComponentIsEnabled = + listOf( + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + componentIsEnabled = true, + expected = true, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, + componentIsEnabled = true, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + componentIsEnabled = true, + expected = false, + ), + TestCase( + componentEnabledSetting = + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + componentIsEnabled = true, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + componentIsEnabled = true, + expected = true, + ), + ) + + private val testDataComponentIsDisabled = + listOf( + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + componentIsEnabled = false, + expected = true, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, + componentIsEnabled = false, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + componentIsEnabled = false, + expected = false, + ), + TestCase( + componentEnabledSetting = + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + componentIsEnabled = false, + expected = false, + ), + TestCase( + componentEnabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, + componentIsEnabled = false, + expected = false, + ), + ) + + private val testData = testDataComponentIsDisabled + testDataComponentIsEnabled + + private val TEST_COMPONENT = ComponentName("pkg", "cls") + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 8f725bebfb16..ef3a33248597 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -16,13 +16,19 @@ package com.android.systemui.volume; +import static android.media.AudioManager.RINGER_MODE_NORMAL; +import static android.media.AudioManager.RINGER_MODE_SILENT; +import static android.media.AudioManager.RINGER_MODE_VIBRATE; + import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -293,7 +299,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectVibrateFromDrawer() { final State initialUnsetState = new State(); - initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); mActiveRinger.performClick(); @@ -307,7 +313,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { @Test public void testSelectMuteFromDrawer() { final State initialUnsetState = new State(); - initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL; + initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL; mDialog.onStateChangedH(initialUnsetState); mActiveRinger.performClick(); @@ -329,7 +335,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { // Make sure we've actually changed the ringer mode. verify(mVolumeDialogController, times(1)).setRingerMode( - AudioManager.RINGER_MODE_NORMAL, false); + RINGER_MODE_NORMAL, false); } /** @@ -511,6 +517,87 @@ public class VolumeDialogImplTest extends SysuiTestCase { } } + private enum RingerDrawerState {INIT, OPEN, CLOSE} + + @Test + public void ringerModeNormal_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT); + } + + @Test + public void ringerModeSilent_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT); + } + + @Test + public void ringerModeVibrate_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT); + } + + @Test + public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN); + } + + @Test + public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE); + } + + @Test + public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE); + } + + @Test + public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() { + assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE); + } + + /** + * The content description should include ringer state, and the correct one. + */ + private void assertRingerContainerDescribesItsState(int ringerMode, + RingerDrawerState drawerState) { + State state = createShellState(); + state.ringerModeInternal = ringerMode; + mDialog.onStateChangedH(state); + + mDialog.show(SHOW_REASON_UNKNOWN); + + if (drawerState != RingerDrawerState.INIT) { + // in both cases we first open the drawer + mDialog.toggleRingerDrawer(true); + + if (drawerState == RingerDrawerState.CLOSE) { + mDialog.toggleRingerDrawer(false); + } + } + + String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription(); + assumeNotNull(ringerContainerDescription); + + String ringerDescription = mContext.getString( + mDialog.getStringDescriptionResourceForRingerMode(ringerMode)); + + if (drawerState == RingerDrawerState.OPEN) { + assertEquals(ringerDescription, ringerContainerDescription); + } else { + assertNotSame(ringerDescription, ringerContainerDescription); + assertTrue(ringerContainerDescription.startsWith(ringerDescription)); + } + } + @After public void teardown() { if (mDialog != null) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeInstalledTilesComponentRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeInstalledTilesComponentRepository.kt new file mode 100644 index 000000000000..ff6b7d083df7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/FakeInstalledTilesComponentRepository.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.pipeline.data.repository + +import android.content.ComponentName +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +class FakeInstalledTilesComponentRepository : InstalledTilesComponentRepository { + + private val installedComponentsPerUser = + mutableMapOf<Int, MutableStateFlow<Set<ComponentName>>>() + + override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> { + return getFlow(userId).asStateFlow() + } + + fun setInstalledPackagesForUser(userId: Int, components: Set<ComponentName>) { + getFlow(userId).value = components + } + + private fun getFlow(userId: Int): MutableStateFlow<Set<ComponentName>> = + installedComponentsPerUser.getOrPut(userId) { MutableStateFlow(emptySet()) } +} diff --git a/packages/overlays/NotesRoleEnabledOverlay/Android.bp b/packages/overlays/NotesRoleEnabledOverlay/Android.bp index 68ebd9652399..70b783f75ec7 100644 --- a/packages/overlays/NotesRoleEnabledOverlay/Android.bp +++ b/packages/overlays/NotesRoleEnabledOverlay/Android.bp @@ -25,6 +25,7 @@ package { runtime_resource_overlay { name: "NotesRoleEnabledOverlay", + certificate: "platform", theme: "NotesRoleEnabled", product_specific: true, } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ad038d1c0361..fdb28ba9103e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -4006,17 +4006,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId) throws RemoteException { - mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + mSecurityPolicy.checkForAccessibilityPermissionOrRole(); if (client == null) { return false; } if (displayId < 0) { throw new IllegalArgumentException("The display id " + displayId + " is invalid."); } - if (displayId == Display.DEFAULT_DISPLAY) { - throw new IllegalArgumentException("The default display cannot be proxy-ed."); - } if (!isTrackedDisplay(displayId)) { throw new IllegalArgumentException("The display " + displayId + " does not exist or is" + " not tracked by accessibility."); @@ -4025,6 +4022,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub throw new IllegalArgumentException("The display " + displayId + " is already being" + " proxy-ed"); } + if (!mProxyManager.displayBelongsToCaller(Binder.getCallingUid(), displayId)) { + throw new SecurityException("The display " + displayId + " does not belong to" + + " the caller."); + } final long identity = Binder.clearCallingIdentity(); try { @@ -4042,8 +4043,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean unregisterProxyForDisplay(int displayId) { - mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + mSecurityPolicy.checkForAccessibilityPermissionOrRole(); final long identity = Binder.clearCallingIdentity(); try { return mProxyManager.unregisterProxy(displayId); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java index 93356263b897..f45fa921c4a2 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java @@ -18,6 +18,7 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_FAIL_BY_ADMIN; import static android.accessibilityservice.AccessibilityService.SoftKeyboardController.ENABLE_IME_SUCCESS; +import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; import android.Manifest; import android.accessibilityservice.AccessibilityService; @@ -25,6 +26,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; +import android.app.role.RoleManager; import android.appwidget.AppWidgetManagerInternal; import android.content.ComponentName; import android.content.Context; @@ -675,6 +677,42 @@ public class AccessibilitySecurityPolicy { } /** + * Throws a SecurityException if the caller has neither the MANAGE_ACCESSIBILITY permission nor + * the COMPANION_DEVICE_APP_STREAMING role. + */ + public void checkForAccessibilityPermissionOrRole() { + final boolean canManageAccessibility = + mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + == PackageManager.PERMISSION_GRANTED; + if (canManageAccessibility) { + return; + } + final int callingUid = Binder.getCallingUid(); + final long identity = Binder.clearCallingIdentity(); + try { + final RoleManager roleManager = mContext.getSystemService(RoleManager.class); + if (roleManager != null) { + final List<String> holders = roleManager.getRoleHoldersAsUser( + DEVICE_PROFILE_APP_STREAMING, UserHandle.getUserHandleForUid(callingUid)); + final String[] packageNames = mPackageManager.getPackagesForUid(callingUid); + if (packageNames != null) { + for (String packageName : packageNames) { + if (holders.contains(packageName)) { + return; + } + } + } + } + throw new SecurityException( + "Cannot register a proxy for a device without the " + + "android.app.role.COMPANION_DEVICE_APP_STREAMING role or the" + + " MANAGE_ACCESSIBILITY permission."); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** * Called after a service was bound or unbound. Checks the current bound accessibility * services and updates alarms. * diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java index 6dc8fb347904..70882c6471de 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java @@ -24,6 +24,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityTrace; import android.accessibilityservice.IAccessibilityServiceClient; import android.annotation.NonNull; +import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.content.ComponentName; import android.content.Context; @@ -319,6 +320,25 @@ public class ProxyManager { return isTrackingDeviceId; } + /** Returns true if the display belongs to one of the caller's virtual devices. */ + public boolean displayBelongsToCaller(int callingUid, int proxyDisplayId) { + final VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class); + final VirtualDeviceManagerInternal localVdm = getLocalVdm(); + if (vdm == null || localVdm == null) { + return false; + } + final List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); + for (VirtualDevice device : virtualDevices) { + if (localVdm.getDisplayIdsForDevice(device.getDeviceId()).contains(proxyDisplayId)) { + final int ownerUid = localVdm.getDeviceOwnerUid(device.getDeviceId()); + if (callingUid == ownerUid) { + return true; + } + } + } + return false; + } + /** * Sends AccessibilityEvents to a proxy given the event's displayId. */ diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 1a57bc1b7a0d..ec4203e3390c 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -63,6 +63,7 @@ import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; import android.util.ArrayMap; import android.util.LocalLog; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -334,7 +335,8 @@ public final class AutofillManagerService // of time. synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.onSwitchInputMethod(); } @@ -366,11 +368,12 @@ public final class AutofillManagerService boolean isTemporary) { mAugmentedAutofillState.setServiceInfo(userId, serviceName, isTemporary); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service == null) { // If we cannot get the service from the services cache, it will call // updateRemoteAugmentedAutofillService() finally. Skip call this update again. - getServiceForUserLocked(userId); + getServiceForUserWithLocalBinderIdentityLocked(userId); } else { service.updateRemoteAugmentedAutofillService(); } @@ -380,17 +383,46 @@ public final class AutofillManagerService private void onFieldClassificationServiceNameChanged( @UserIdInt int userId, @Nullable String serviceName, boolean isTemporary) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service == null) { // If we cannot get the service from the services cache, it will call // updateRemoteFieldClassificationService() finally. Skip call this update again. - getServiceForUserLocked(userId); + getServiceForUserWithLocalBinderIdentityLocked(userId); } else { service.updateRemoteFieldClassificationService(); } } } + @GuardedBy("mLock") + @Nullable + private AutofillManagerServiceImpl getServiceForUserWithLocalBinderIdentityLocked(int userId) { + final long token = Binder.clearCallingIdentity(); + AutofillManagerServiceImpl managerService = null; + try { + managerService = getServiceForUserLocked(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + + return managerService; + } + + @GuardedBy("mLock") + @Nullable + private AutofillManagerServiceImpl peekServiceForUserWithLocalBinderIdentityLocked(int userId) { + final long token = Binder.clearCallingIdentity(); + AutofillManagerServiceImpl managerService = null; + try { + managerService = peekServiceForUserLocked(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + + return managerService; + } + @Override // from AbstractMasterSystemService protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { @@ -1038,7 +1070,8 @@ public final class AutofillManagerService mUi.hideAll(null); synchronized (mLock) { final AutofillManagerServiceImpl service = - getServiceForUserLocked(UserHandle.getCallingUserId()); + getServiceForUserWithLocalBinderIdentityLocked( + UserHandle.getCallingUserId()); service.onBackKeyPressed(); } } @@ -1537,20 +1570,27 @@ public final class AutofillManagerService public void addClient(IAutoFillManagerClient client, ComponentName componentName, int userId, IResultReceiver receiver) { int flags = 0; - synchronized (mLock) { - final int enabledFlags = getServiceForUserLocked(userId).addClientLocked(client, - componentName); - if (enabledFlags != 0) { - flags |= enabledFlags; - } - if (sDebug) { - flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG; - } - if (sVerbose) { - flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE; + try { + synchronized (mLock) { + final int enabledFlags = + getServiceForUserWithLocalBinderIdentityLocked(userId) + .addClientLocked(client, componentName); + if (enabledFlags != 0) { + flags |= enabledFlags; + } + if (sDebug) { + flags |= AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } + if (sVerbose) { + flags |= AutofillManager.FLAG_ADD_CLIENT_VERBOSE; + } } + } catch (Exception ex) { + // Don't do anything, send back default flags + Log.wtf(TAG, "addClient(): failed " + ex.toString()); + } finally { + send(receiver, flags); } - send(receiver, flags); } @Override @@ -1569,7 +1609,8 @@ public final class AutofillManagerService public void setAuthenticationResult(Bundle data, int sessionId, int authenticationId, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); service.setAuthenticationResultLocked(data, sessionId, authenticationId, getCallingUid()); } @@ -1578,7 +1619,8 @@ public final class AutofillManagerService @Override public void setHasCallback(int sessionId, int userId, boolean hasIt) { synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); service.setHasCallback(sessionId, getCallingUid(), hasIt); } } @@ -1608,7 +1650,8 @@ public final class AutofillManagerService final int taskId = mAm.getTaskIdForActivity(activityToken, false); final long result; synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + getServiceForUserWithLocalBinderIdentityLocked(userId); result = service.startSessionLocked(activityToken, taskId, getCallingUid(), clientCallback, autofillId, bounds, value, hasCallback, clientActivity, compatMode, mAllowInstantService, flags); @@ -1624,51 +1667,72 @@ public final class AutofillManagerService @Override public void getFillEventHistory(@NonNull IResultReceiver receiver) throws RemoteException { + FillEventHistory fillEventHistory = null; final int userId = UserHandle.getCallingUserId(); - FillEventHistory fillEventHistory = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - fillEventHistory = service.getFillEventHistory(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getFillEventHistory(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + fillEventHistory = service.getFillEventHistory(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getFillEventHistory(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getFillEventHistory(): failed " + ex.toString()); + } finally { + send(receiver, fillEventHistory); } - send(receiver, fillEventHistory); } @Override public void getUserData(@NonNull IResultReceiver receiver) throws RemoteException { + UserData userData = null; final int userId = UserHandle.getCallingUserId(); - UserData userData = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - userData = service.getUserData(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getUserData(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + userData = service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserData(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getUserData(): failed " + ex.toString()); + } finally { + send(receiver, userData); } - send(receiver, userData); } @Override public void getUserDataId(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); UserData userData = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - userData = service.getUserData(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "getUserDataId(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + userData = service.getUserData(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "getUserDataId(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back the null response + Log.wtf(TAG, "getUserDataId(): failed " + ex.toString()); + } finally { + final String userDataId = userData == null ? null : userData.getId(); + send(receiver, userDataId); } - final String userDataId = userData == null ? null : userData.getId(); - send(receiver, userDataId); } @Override @@ -1676,7 +1740,8 @@ public final class AutofillManagerService final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.setUserData(getCallingUid(), userData); } else if (sVerbose) { @@ -1688,124 +1753,171 @@ public final class AutofillManagerService @Override public void isFieldClassificationEnabled(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); boolean enabled = false; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - enabled = service.isFieldClassificationEnabled(getCallingUid()); - } else if (sVerbose) { - Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + enabled = service.isFieldClassificationEnabled(getCallingUid()); + } else if (sVerbose) { + Slog.v(TAG, "isFieldClassificationEnabled(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not raise the exception, just send back false + Log.wtf(TAG, "isFieldClassificationEnabled(): failed " + ex.toString()); + } finally { + send(receiver, enabled); } - send(receiver, enabled); } @Override public void getDefaultFieldClassificationAlgorithm(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); String algorithm = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + algorithm = service.getDefaultFieldClassificationAlgorithm(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId); + } } - } + } + } catch (Exception ex) { + // Do not raise the exception, just send back null + Log.wtf(TAG, "getDefaultFieldClassificationAlgorithm(): failed " + ex.toString()); + } finally { + send(receiver, algorithm); } - send(receiver, algorithm); + } @Override public void setAugmentedAutofillWhitelist(@Nullable List<String> packages, @Nullable List<ComponentName> activities, @NonNull IResultReceiver receiver) throws RemoteException { + boolean ok = false; final int userId = UserHandle.getCallingUserId(); - boolean ok; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - ok = service.setAugmentedAutofillWhitelistLocked(packages, activities, - getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + ok = service.setAugmentedAutofillWhitelistLocked(packages, activities, + getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "setAugmentedAutofillWhitelist(): no service for " + + userId); + } } - ok = false; } + } catch (Exception ex) { + // Do not raise the exception, return the default value + Log.wtf(TAG, "setAugmentedAutofillWhitelist(): failed " + ex.toString()); + } finally { + send(receiver, + ok ? AutofillManager.RESULT_OK + : AutofillManager.RESULT_CODE_NOT_SERVICE); } - send(receiver, - ok ? AutofillManager.RESULT_OK : AutofillManager.RESULT_CODE_NOT_SERVICE); } @Override public void getAvailableFieldClassificationAlgorithms(@NonNull IResultReceiver receiver) throws RemoteException { - final int userId = UserHandle.getCallingUserId(); String[] algorithms = null; + final int userId = UserHandle.getCallingUserId(); - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - algorithms = service.getAvailableFieldClassificationAlgorithms(getCallingUid()); - } else { - if (sVerbose) { - Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + algorithms = service + .getAvailableFieldClassificationAlgorithms(getCallingUid()); + } else { + if (sVerbose) { + Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId); + } } } + } catch (Exception ex) { + // Do not raise the exception, return null + Log.wtf(TAG, "getAvailableFieldClassificationAlgorithms(): failed " + + ex.toString()); + } finally { + send(receiver, algorithms); } - send(receiver, algorithms); } @Override public void getAutofillServiceComponentName(@NonNull IResultReceiver receiver) throws RemoteException { + ComponentName componentName = null; final int userId = UserHandle.getCallingUserId(); - ComponentName componentName = null; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - componentName = service.getServiceComponentName(); - } else if (sVerbose) { - Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + componentName = service.getServiceComponentName(); + } else if (sVerbose) { + Slog.v(TAG, "getAutofillServiceComponentName(): no service for " + userId); + } } + } catch (Exception ex) { + Log.wtf(TAG, "getAutofillServiceComponentName(): failed " + ex.toString()); + } finally { + send(receiver, componentName); } - send(receiver, componentName); } @Override public void restoreSession(int sessionId, @NonNull IBinder activityToken, @NonNull IBinder appCallback, @NonNull IResultReceiver receiver) throws RemoteException { + boolean restored = false; final int userId = UserHandle.getCallingUserId(); - Objects.requireNonNull(activityToken, "activityToken"); - Objects.requireNonNull(appCallback, "appCallback"); - boolean restored = false; - synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); - if (service != null) { - restored = service.restoreSession(sessionId, getCallingUid(), activityToken, - appCallback); - } else if (sVerbose) { - Slog.v(TAG, "restoreSession(): no service for " + userId); + try { + Objects.requireNonNull(activityToken, "activityToken"); + Objects.requireNonNull(appCallback, "appCallback"); + + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + if (service != null) { + restored = service.restoreSession(sessionId, getCallingUid(), activityToken, + appCallback); + } else if (sVerbose) { + Slog.v(TAG, "restoreSession(): no service for " + userId); + } } + } catch (Exception ex) { + // Do not propagate exception, send back status + Log.wtf(TAG, "restoreSession(): failed " + ex.toString()); + } finally { + send(receiver, restored); } - send(receiver, restored); } @Override public void updateSession(int sessionId, AutofillId autoFillId, Rect bounds, AutofillValue value, int action, int flags, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.updateSessionLocked(sessionId, getCallingUid(), autoFillId, bounds, value, action, flags); @@ -1818,7 +1930,8 @@ public final class AutofillManagerService @Override public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.setAutofillFailureLocked(sessionId, getCallingUid(), ids); } else if (sVerbose) { @@ -1831,7 +1944,8 @@ public final class AutofillManagerService public void finishSession(int sessionId, int userId, @AutofillCommitReason int commitReason) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.finishSessionLocked(sessionId, getCallingUid(), commitReason); } else if (sVerbose) { @@ -1843,19 +1957,22 @@ public final class AutofillManagerService @Override public void cancelSession(int sessionId, int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.cancelSessionLocked(sessionId, getCallingUid()); } else if (sVerbose) { Slog.v(TAG, "cancelSession(): no service for " + userId); } } + } @Override public void disableOwnedAutofillServices(int userId) { synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { service.disableOwnedAutofillServicesLocked(Binder.getCallingUid()); } else if (sVerbose) { @@ -1867,21 +1984,36 @@ public final class AutofillManagerService @Override public void isServiceSupported(int userId, @NonNull IResultReceiver receiver) { boolean supported = false; - synchronized (mLock) { - supported = !isDisabledLocked(userId); + + try { + synchronized (mLock) { + supported = !isDisabledLocked(userId); + } + } catch (Exception ex) { + // Do not propagate exception + Log.wtf(TAG, "isServiceSupported(): failed " + ex.toString()); + } finally { + send(receiver, supported); } - send(receiver, supported); } @Override public void isServiceEnabled(int userId, @NonNull String packageName, @NonNull IResultReceiver receiver) { boolean enabled = false; - synchronized (mLock) { - final AutofillManagerServiceImpl service = getServiceForUserLocked(userId); - enabled = Objects.equals(packageName, service.getServicePackageName()); + + try { + synchronized (mLock) { + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked(userId); + enabled = Objects.equals(packageName, service.getServicePackageName()); + } + } catch (Exception ex) { + // Do not propagate exception + Log.wtf(TAG, "isServiceEnabled(): failed " + ex.toString()); + } finally { + send(receiver, enabled); } - send(receiver, enabled); } @Override @@ -1891,8 +2023,9 @@ public final class AutofillManagerService || operation == AutofillManager.PENDING_UI_OPERATION_RESTORE, "invalid operation: %d", operation); synchronized (mLock) { - final AutofillManagerServiceImpl service = peekServiceForUserLocked( - UserHandle.getCallingUserId()); + final AutofillManagerServiceImpl service = + peekServiceForUserWithLocalBinderIdentityLocked( + UserHandle.getCallingUserId()); if (service != null) { service.onPendingSaveUi(operation, token); } @@ -1907,7 +2040,7 @@ public final class AutofillManagerService boolean uiOnly = false; if (args != null) { for (String arg : args) { - switch(arg) { + switch (arg) { case "--no-history": showHistory = false; break; @@ -1934,27 +2067,38 @@ public final class AutofillManagerService try { sDebug = sVerbose = true; synchronized (mLock) { - pw.print("sDebug: "); pw.print(realDebug); - pw.print(" sVerbose: "); pw.println(realVerbose); + pw.print("sDebug: "); + pw.print(realDebug); + pw.print(" sVerbose: "); + pw.println(realVerbose); pw.print("Flags: "); synchronized (mFlagLock) { - pw.print("mPccClassificationEnabled="); pw.print(mPccClassificationEnabled); + pw.print("mPccClassificationEnabled="); + pw.print(mPccClassificationEnabled); pw.print(";"); - pw.print("mPccPreferProviderOverPcc="); pw.print(mPccPreferProviderOverPcc); + pw.print("mPccPreferProviderOverPcc="); + pw.print(mPccPreferProviderOverPcc); pw.print(";"); - pw.print("mPccUseFallbackDetection="); pw.print(mPccUseFallbackDetection); + pw.print("mPccUseFallbackDetection="); + pw.print(mPccUseFallbackDetection); pw.print(";"); - pw.print("mPccProviderHints="); pw.println(mPccProviderHints); + pw.print("mPccProviderHints="); + pw.println(mPccProviderHints); } // Dump per-user services dumpLocked("", pw); - mAugmentedAutofillResolver.dumpShort(pw); pw.println(); - pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount); - pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount); + mAugmentedAutofillResolver.dumpShort(pw); + pw.println(); + pw.print("Max partitions per session: "); + pw.println(sPartitionMaxCount); + pw.print("Max visible datasets: "); + pw.println(sVisibleDatasetsMaxCount); if (sFullScreenMode != null) { - pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode); + pw.print("Overridden full-screen mode: "); + pw.println(sFullScreenMode); } - pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw); + pw.println("User data constraints: "); + UserData.dumpConstraints(prefix, pw); mUi.dump(pw); pw.print("Autofill Compat State: "); mAutofillCompatState.dump(prefix, pw); @@ -1969,11 +2113,17 @@ public final class AutofillManagerService pw.print("Augmented Service Request Timeout: "); pw.println(mAugmentedServiceRequestTimeoutMs); if (showHistory) { - pw.println(); pw.println("Requests history:"); pw.println(); + pw.println(); + pw.println("Requests history:"); + pw.println(); mRequestsHistory.reverseDump(fd, pw, args); - pw.println(); pw.println("UI latency history:"); pw.println(); + pw.println(); + pw.println("UI latency history:"); + pw.println(); mUiLatencyHistory.reverseDump(fd, pw, args); - pw.println(); pw.println("WTF history:"); pw.println(); + pw.println(); + pw.println("WTF history:"); + pw.println(); mWtfHistory.reverseDump(fd, pw, args); } pw.println("Augmented Autofill State: "); diff --git a/services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java b/services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java new file mode 100644 index 000000000000..b4aca1530204 --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/LogFieldClassificationScoreOnResultListener.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.autofill; + +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.RemoteCallback; +import android.service.autofill.FieldClassification; +import android.service.autofill.FillEventHistory.Event.NoSaveReason; +import android.util.Slog; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager.AutofillCommitReason; + +import java.util.ArrayList; + +class LogFieldClassificationScoreOnResultListener implements + RemoteCallback.OnResultListener { + + private static final String TAG = "LogFieldClassificationScoreOnResultListener"; + + private Session mSession; + private final @NoSaveReason int mSaveDialogNotShowReason; + private final @AutofillCommitReason int mCommitReason; + private final int mViewsSize; + private final AutofillId[] mAutofillIds; + private final String[] mUserValues; + private final String[] mCategoryIds; + private final ArrayList<AutofillId> mDetectedFieldIds; + private final ArrayList<FieldClassification> mDetectedFieldClassifications; + LogFieldClassificationScoreOnResultListener(Session session, + int saveDialogNotShowReason, + int commitReason, int viewsSize, AutofillId[] autofillIds, String[] userValues, + String[] categoryIds, ArrayList<AutofillId> detectedFieldIds, + ArrayList<FieldClassification> detectedFieldClassifications) { + this.mSession = session; + this.mSaveDialogNotShowReason = saveDialogNotShowReason; + this.mCommitReason = commitReason; + this.mViewsSize = viewsSize; + this.mAutofillIds = autofillIds; + this.mUserValues = userValues; + this.mCategoryIds = categoryIds; + this.mDetectedFieldIds = detectedFieldIds; + this.mDetectedFieldClassifications = detectedFieldClassifications; + } + + public void onResult(@Nullable Bundle result) { + // Create a local copy to safe guard race condition + Session session = mSession; + if (session == null) { + Slog.wtf(TAG, "session is null when calling onResult()"); + return; + } + session.handleLogFieldClassificationScore( + result, + mSaveDialogNotShowReason, + mCommitReason, + mViewsSize, + mAutofillIds, + mUserValues, + mCategoryIds, + mDetectedFieldIds, + mDetectedFieldClassifications); + mSession = null; + } +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4576abb4a730..0c3f8667f4f8 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -3063,76 +3063,91 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } // Then use the results, asynchronously - final RemoteCallback callback = new RemoteCallback((result) -> { - 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); - if (scores == null) { - Slog.w(TAG, "No field classification score on " + result); - return; - } - int i = 0, j = 0; - try { - // Iteract over all autofill fields first - for (i = 0; i < viewsSize; i++) { - final AutofillId autofillId = autofillIds[i]; - - // Search the best scores for each category (as some categories could have - // multiple user values - ArrayMap<String, Float> scoresByField = null; - for (j = 0; j < userValues.length; j++) { - final String categoryId = categoryIds[j]; - final float score = scores.scores[i][j]; - if (score > 0) { - if (scoresByField == null) { - scoresByField = new ArrayMap<>(userValues.length); - } - 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); - } - continue; - } + 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); + if (scores == null) { + Slog.w(TAG, "No field classification score on " + result); + return; + } + int i = 0, j = 0; + try { + // Iteract over all autofill fields first + for (i = 0; i < viewsSize; i++) { + final AutofillId autofillId = autofillIds[i]; + + // Search the best scores for each category (as some categories could have + // multiple user values + ArrayMap<String, Float> scoresByField = null; + for (j = 0; j < userValues.length; j++) { + final String categoryId = categoryIds[j]; + final float score = scores.scores[i][j]; + if (score > 0) { + if (scoresByField == null) { + scoresByField = new ArrayMap<>(userValues.length); + } + final Float currentScore = scoresByField.get(categoryId); + if (currentScore != null && currentScore > score) { if (sVerbose) { - Slog.v(TAG, "adding score " + score + " at index " + j + " and id " - + autofillId); + Slog.v(TAG, "skipping score " + score + + " because it's less than " + currentScore); } - scoresByField.put(categoryId, score); - } else if (sVerbose) { - Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId); + continue; } + if (sVerbose) { + Slog.v(TAG, "adding score " + score + " at index " + j + " and id " + + autofillId); + } + scoresByField.put(categoryId, score); + } else if (sVerbose) { + Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId); } - if (scoresByField == null) { - if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId); - continue; - } - - // Then create the matches for that autofill id - final ArrayList<Match> matches = new ArrayList<>(scoresByField.size()); - for (j = 0; j < scoresByField.size(); j++) { - final String fieldId = scoresByField.keyAt(j); - final float score = scoresByField.valueAt(j); - matches.add(new Match(fieldId, score)); - } - detectedFieldIds.add(autofillId); - detectedFieldClassifications.add(new FieldClassification(matches)); - } // for i - } catch (ArrayIndexOutOfBoundsException e) { - wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e); - return; - } - - logContextCommitted(detectedFieldIds, detectedFieldClassifications, - saveDialogNotShowReason, commitReason); - }); + } + if (scoresByField == null) { + if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId); + continue; + } - fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, - defaultAlgorithm, defaultArgs, algorithms, args); + // Then create the matches for that autofill id + final ArrayList<Match> matches = new ArrayList<>(scoresByField.size()); + for (j = 0; j < scoresByField.size(); j++) { + final String fieldId = scoresByField.keyAt(j); + final float score = scoresByField.valueAt(j); + matches.add(new Match(fieldId, score)); + } + detectedFieldIds.add(autofillId); + detectedFieldClassifications.add(new FieldClassification(matches)); + } // for i + } catch (ArrayIndexOutOfBoundsException e) { + wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e); + return; + } + logContextCommitted(detectedFieldIds, detectedFieldClassifications, + saveDialogNotShowReason, commitReason); } /** @@ -4857,16 +4872,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - final boolean isWhitelisted = mService + final boolean isAllowlisted = mService .isWhitelistedForAugmentedAutofillLocked(mComponentName); - if (!isWhitelisted) { + if (!isAllowlisted) { if (sVerbose) { Slog.v(TAG, "triggerAugmentedAutofillLocked(): " + ComponentName.flattenToShortString(mComponentName) + " not whitelisted "); } logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - mCurrentViewId, isWhitelisted, /* isInline= */ null); + mCurrentViewId, isAllowlisted, /* isInline= */ null); return null; } @@ -4899,32 +4914,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId focusedId = mCurrentViewId; - final Function<InlineFillUi, Boolean> inlineSuggestionsResponseCallback = - response -> { - synchronized (mLock) { - return mInlineSessionController.setInlineFillUiLocked(response); - } - }; final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill = - (inlineSuggestionsRequest) -> { - synchronized (mLock) { - logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), - focusedId, isWhitelisted, inlineSuggestionsRequest != null); - remoteService.onRequestAutofillLocked(id, mClient, - taskId, mComponentName, mActivityToken, - AutofillId.withoutSession(focusedId), currentValue, - inlineSuggestionsRequest, inlineSuggestionsResponseCallback, - /*onErrorCallback=*/ () -> { - synchronized (mLock) { - cancelAugmentedAutofillLocked(); - - // Also cancel augmented in IME - mInlineSessionController.setInlineFillUiLocked( - InlineFillUi.emptyUi(mCurrentViewId)); - } - }, mService.getRemoteInlineSuggestionRenderServiceLocked(), userId); - } - }; + new AugmentedAutofillInlineSuggestionRequestConsumer( + this, focusedId, isAllowlisted, mode, currentValue); // When the inline suggestion render service is available and the view is focused, there // are 3 cases when augmented autofill should ask IME for inline suggestion request, @@ -4942,14 +4934,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState || mSessionFlags.mExpiredResponse) && (isViewFocusedLocked(flags) || isRequestSupportFillDialog(flags))) { if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill"); - remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback( - (extras) -> { - synchronized (mLock) { - mInlineSessionController.onCreateInlineSuggestionsRequestLocked( - focusedId, /*requestConsumer=*/ requestAugmentedAutofill, - extras); - } - }, mHandler)); + remoteRenderService.getInlineSuggestionsRendererInfo( + new RemoteCallback( + new AugmentedAutofillInlineSuggestionRendererOnResultListener( + this, focusedId, requestAugmentedAutofill), + mHandler)); } else { requestAugmentedAutofill.accept( mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null)); @@ -4960,6 +4949,169 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return mAugmentedAutofillDestroyer; } + private static class AugmentedAutofillInlineSuggestionRendererOnResultListener + implements RemoteCallback.OnResultListener { + + WeakReference<Session> mSessionWeakRef; + final AutofillId mFocusedId; + Consumer<InlineSuggestionsRequest> mRequestAugmentedAutofill; + + AugmentedAutofillInlineSuggestionRendererOnResultListener( + Session session, + AutofillId focussedId, + Consumer<InlineSuggestionsRequest> requestAugmentedAutofill) { + mSessionWeakRef = new WeakReference<>(session); + mFocusedId = focussedId; + mRequestAugmentedAutofill = requestAugmentedAutofill; + } + + @Override + public void onResult(@Nullable Bundle result) { + Session session = mSessionWeakRef.get(); + + if (logIfSessionNull( + session, "AugmentedAutofillInlineSuggestionRendererOnResultListener:")) { + return; + } + synchronized (session.mLock) { + session.mInlineSessionController.onCreateInlineSuggestionsRequestLocked( + mFocusedId, /*requestConsumer=*/ mRequestAugmentedAutofill, + result); + } + } + } + + private static class AugmentedAutofillInlineSuggestionRequestConsumer + implements Consumer<InlineSuggestionsRequest> { + + WeakReference<Session> mSessionWeakRef; + final AutofillId mFocusedId; + final boolean mIsAllowlisted; + final int mMode; + final AutofillValue mCurrentValue; + + AugmentedAutofillInlineSuggestionRequestConsumer( + Session session, + AutofillId focussedId, + boolean isAllowlisted, + int mode, + AutofillValue currentValue) { + mSessionWeakRef = new WeakReference<>(session); + mFocusedId = focussedId; + mIsAllowlisted = isAllowlisted; + mMode = mode; + mCurrentValue = currentValue; + + } + @Override + public void accept(InlineSuggestionsRequest inlineSuggestionsRequest) { + Session session = mSessionWeakRef.get(); + + if (logIfSessionNull( + session, "AugmentedAutofillInlineSuggestionRequestConsumer:")) { + return; + } + session.onAugmentedAutofillInlineSuggestionAccept( + inlineSuggestionsRequest, mFocusedId, mIsAllowlisted, mMode, mCurrentValue); + + } + } + + private static class AugmentedAutofillInlineSuggestionsResponseCallback + implements Function<InlineFillUi, Boolean> { + + WeakReference<Session> mSessionWeakRef; + + AugmentedAutofillInlineSuggestionsResponseCallback(Session session) { + this.mSessionWeakRef = new WeakReference<>(session); + } + + @Override + public Boolean apply(InlineFillUi inlineFillUi) { + Session session = mSessionWeakRef.get(); + + if (logIfSessionNull( + session, "AugmentedAutofillInlineSuggestionsResponseCallback:")) { + return false; + } + + synchronized (session.mLock) { + return session.mInlineSessionController.setInlineFillUiLocked(inlineFillUi); + } + } + } + + private static class AugmentedAutofillErrorCallback implements Runnable { + + WeakReference<Session> mSessionWeakRef; + + AugmentedAutofillErrorCallback(Session session) { + this.mSessionWeakRef = new WeakReference<>(session); + } + + @Override + public void run() { + Session session = mSessionWeakRef.get(); + + if (logIfSessionNull(session, "AugmentedAutofillErrorCallback:")) { + return; + } + session.onAugmentedAutofillErrorCallback(); + } + } + + /** + * 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. + */ + private static boolean logIfSessionNull(Session session, String logPrefix) { + if (session == null) { + Slog.wtf(TAG, logPrefix + " Session null"); + return true; + } + if (session.mDestroyed) { + // TODO: Update this to return in this block. We aren't doing this to preserve the + // behavior, but can be modified once we have more time to soak the changes. + Slog.w(TAG, logPrefix + " Session destroyed, but following through"); + // Follow-through + } + return false; + } + + private void onAugmentedAutofillInlineSuggestionAccept( + InlineSuggestionsRequest inlineSuggestionsRequest, + AutofillId focussedId, + boolean isAllowlisted, + int mode, + AutofillValue currentValue) { + 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, + inlineSuggestionsRequest, + new AugmentedAutofillInlineSuggestionsResponseCallback(this), + new AugmentedAutofillErrorCallback(this), + mService.getRemoteInlineSuggestionRenderServiceLocked(), userId); + } + } + + private void onAugmentedAutofillErrorCallback() { + synchronized (mLock) { + cancelAugmentedAutofillLocked(); + + // Also cancel augmented in IME + mInlineSessionController.setInlineFillUiLocked( + InlineFillUi.emptyUi(mCurrentViewId)); + } + } + @GuardedBy("mLock") private void cancelAugmentedAutofillLocked() { final RemoteAugmentedAutofillService remoteService = mService diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 6b99494f6564..a1ccade51214 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -1415,6 +1415,30 @@ public class CompanionDeviceManagerService extends SystemService { mCrossDeviceSyncController.syncMessageToDevice(associationId, message); } } + + @Override + public void sendCrossDeviceSyncMessageToAllDevices(int userId, byte[] message) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.syncMessageToAllDevicesForUserId(userId, message); + } + } + + @Override + public void addSelfOwnedCallId(String callId) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.addSelfOwnedCallId(callId); + } + } + + @Override + public void removeSelfOwnedCallId(String callId) { + if (CompanionDeviceConfig.isEnabled( + CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM)) { + mCrossDeviceSyncController.removeSelfOwnedCallId(callId); + } + } } /** diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java index c5ef4e49e5ea..cdf832f8c788 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerServiceInternal.java @@ -45,6 +45,15 @@ public interface CompanionDeviceManagerServiceInternal { */ void sendCrossDeviceSyncMessage(int associationId, byte[] message); + /** Sends the provided message to all active associations for the specified user. */ + void sendCrossDeviceSyncMessageToAllDevices(int userId, byte[] message); + + /** Mark a call id as "self owned" (i.e. this device owns the canonical call). */ + void addSelfOwnedCallId(String callId); + + /** Unmark a call id as "self owned" (i.e. this device no longer owns the canonical call). */ + void removeSelfOwnedCallId(String callId); + /** * Requests a sync from an InCallService to CDM, for the given user and call metadata. */ diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java index 7371824e9c9c..fac1c8981061 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncConnectionService.java @@ -36,7 +36,7 @@ import com.android.server.companion.CompanionDeviceManagerServiceInternal; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.UUID; +import java.util.Set; /** Service for Telecom to bind to when call metadata is synced between devices. */ public class CallMetadataSyncConnectionService extends ConnectionService { @@ -65,11 +65,32 @@ public class CallMetadataSyncConnectionService extends ConnectionService { associationId, call.getId())); if (existingConnection != null) { existingConnection.update(call); + } else { + // Check if this is an in-progress id being finalized. + CallMetadataSyncConnectionIdentifier key = null; + for (Map.Entry<CallMetadataSyncConnectionIdentifier, + CallMetadataSyncConnection> e : mActiveConnections.entrySet()) { + if (e.getValue().getAssociationId() == associationId + && !e.getValue().isIdFinalized() + && call.getId().endsWith(e.getValue().getCallId())) { + key = e.getKey(); + break; + } + } + if (key != null) { + final CallMetadataSyncConnection connection = + mActiveConnections.remove(key); + connection.update(call); + mActiveConnections.put( + new CallMetadataSyncConnectionIdentifier(associationId, + call.getId()), connection); + } } } // Remove obsolete calls. mActiveConnections.values().removeIf(connection -> { - if (associationId == connection.getAssociationId() + if (connection.isIdFinalized() + && associationId == connection.getAssociationId() && !callMetadataSyncData.hasCall(connection.getCallId())) { connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); return true; @@ -77,6 +98,17 @@ public class CallMetadataSyncConnectionService extends ConnectionService { return false; }); } + + @Override + void cleanUpCallIds(Set<String> callIds) { + mActiveConnections.values().removeIf(connection -> { + if (callIds.contains(connection.getCallId())) { + connection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); + return true; + } + return false; + }); + } }; @Override @@ -95,10 +127,9 @@ public class CallMetadataSyncConnectionService extends ConnectionService { ConnectionRequest connectionRequest) { final int associationId = connectionRequest.getExtras().getInt( CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); - final CallMetadataSyncData.Call call = connectionRequest.getExtras().getParcelable( - CrossDeviceSyncController.EXTRA_CALL, CallMetadataSyncData.Call.class); - // InCallServices outside of framework (like Dialer's) might try to read this, and crash - // when they can't. Remove it once we're done with it, as well as the other internal ones. + final CallMetadataSyncData.Call call = CallMetadataSyncData.Call.fromBundle( + connectionRequest.getExtras().getBundle(CrossDeviceSyncController.EXTRA_CALL)); + call.setDirection(android.companion.Telecom.Call.INCOMING); connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL); connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID); connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); @@ -130,18 +161,26 @@ public class CallMetadataSyncConnectionService extends ConnectionService { @Override public Connection onCreateOutgoingConnection(PhoneAccountHandle phoneAccountHandle, ConnectionRequest connectionRequest) { - final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(phoneAccountHandle); + final PhoneAccountHandle handle = phoneAccountHandle != null ? phoneAccountHandle + : connectionRequest.getAccountHandle(); + final PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle); final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); - call.setId(UUID.randomUUID().toString()); + call.setId( + connectionRequest.getExtras().getString(CrossDeviceSyncController.EXTRA_CALL_ID)); call.setStatus(android.companion.Telecom.Call.UNKNOWN_STATUS); final CallMetadataSyncData.CallFacilitator callFacilitator = - new CallMetadataSyncData.CallFacilitator(phoneAccount.getLabel().toString(), - phoneAccount.getExtras().getString( - CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID)); + new CallMetadataSyncData.CallFacilitator(phoneAccount != null + ? phoneAccount.getLabel().toString() + : handle.getComponentName().getShortClassName(), + phoneAccount != null ? phoneAccount.getExtras().getString( + CrossDeviceSyncController.EXTRA_CALL_FACILITATOR_ID) + : handle.getComponentName().getPackageName()); call.setFacilitator(callFacilitator); + call.setDirection(android.companion.Telecom.Call.OUTGOING); + call.setCallerId(connectionRequest.getAddress().getSchemeSpecificPart()); - final int associationId = connectionRequest.getExtras().getInt( + final int associationId = phoneAccount.getExtras().getInt( CrossDeviceSyncController.EXTRA_ASSOCIATION_ID); connectionRequest.getExtras().remove(CrossDeviceSyncController.EXTRA_CALL); @@ -160,13 +199,15 @@ public class CallMetadataSyncConnectionService extends ConnectionService { CrossDeviceSyncController.createCallControlMessage(callId, action)); } }); - connection.setConnectionProperties(Connection.PROPERTY_IS_EXTERNAL_CALL); + connection.setCallerDisplayName(call.getCallerId(), TelecomManager.PRESENTATION_ALLOWED); + mCdmsi.addSelfOwnedCallId(call.getId()); mCdmsi.sendCrossDeviceSyncMessage(associationId, CrossDeviceSyncController.createCallCreateMessage(call.getId(), connectionRequest.getAddress().toString(), call.getFacilitator().getIdentifier())); + connection.setInitializing(); return connection; } @@ -240,6 +281,7 @@ public class CallMetadataSyncConnectionService extends ConnectionService { private final int mAssociationId; private final CallMetadataSyncData.Call mCall; private final CallMetadataSyncConnectionCallback mCallback; + private boolean mIsIdFinalized; CallMetadataSyncConnection(TelecomManager telecomManager, AudioManager audioManager, int associationId, CallMetadataSyncData.Call call, @@ -259,6 +301,10 @@ public class CallMetadataSyncConnectionService extends ConnectionService { return mAssociationId; } + public boolean isIdFinalized() { + return mIsIdFinalized; + } + private void initialize() { final int status = mCall.getStatus(); if (status == android.companion.Telecom.Call.RINGING_SILENCED) { @@ -273,6 +319,8 @@ public class CallMetadataSyncConnectionService extends ConnectionService { setOnHold(); } else if (state == Call.STATE_DISCONNECTED) { setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); + } else if (state == Call.STATE_DIALING) { + setDialing(); } else { setInitialized(); } @@ -307,6 +355,10 @@ public class CallMetadataSyncConnectionService extends ConnectionService { } private void update(CallMetadataSyncData.Call call) { + if (!mIsIdFinalized) { + mCall.setId(call.getId()); + mIsIdFinalized = true; + } final int status = call.getStatus(); if (status == android.companion.Telecom.Call.RINGING_SILENCED && mCall.getStatus() != android.companion.Telecom.Call.RINGING_SILENCED) { @@ -323,6 +375,8 @@ public class CallMetadataSyncConnectionService extends ConnectionService { setOnHold(); } else if (state == Call.STATE_DISCONNECTED) { setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); + } else if (state == Call.STATE_DIALING) { + setDialing(); } else { Slog.e(TAG, "Could not update call to unknown state"); } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java index d8621cb796ab..74641a48c4dd 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncData.java @@ -16,10 +16,8 @@ package com.android.server.companion.datatransfer.contextsync; -import android.annotation.NonNull; import android.companion.ContextSyncMessage; -import android.os.Parcel; -import android.os.Parcelable; +import android.os.Bundle; import java.util.ArrayList; import java.util.Collection; @@ -74,9 +72,10 @@ class CallMetadataSyncData { return mCallFacilitators; } - public static class CallFacilitator implements Parcelable { + public static class CallFacilitator { private String mName; private String mIdentifier; + private boolean mIsTel; CallFacilitator() {} @@ -85,16 +84,6 @@ class CallMetadataSyncData { mIdentifier = identifier; } - CallFacilitator(Parcel parcel) { - this(parcel.readString(), parcel.readString()); - } - - @Override - public void writeToParcel(Parcel parcel, int parcelableFlags) { - parcel.writeString(mName); - parcel.writeString(mIdentifier); - } - public String getName() { return mName; } @@ -103,6 +92,10 @@ class CallMetadataSyncData { return mIdentifier; } + public boolean isTel() { + return mIsTel; + } + public void setName(String name) { mName = name; } @@ -111,25 +104,9 @@ class CallMetadataSyncData { mIdentifier = identifier; } - @Override - public int describeContents() { - return 0; + public void setIsTel(boolean isTel) { + mIsTel = isTel; } - - @NonNull - public static final Parcelable.Creator<CallFacilitator> CREATOR = - new Parcelable.Creator<>() { - - @Override - public CallFacilitator createFromParcel(Parcel source) { - return new CallFacilitator(source); - } - - @Override - public CallFacilitator[] newArray(int size) { - return new CallFacilitator[size]; - } - }; } public static class CallControlRequest { @@ -183,40 +160,57 @@ class CallMetadataSyncData { } } - public static class Call implements Parcelable { + public static class Call { + + private static final String EXTRA_CALLER_ID = + "com.android.server.companion.datatransfer.contextsync.extra.CALLER_ID"; + private static final String EXTRA_APP_ICON = + "com.android.server.companion.datatransfer.contextsync.extra.APP_ICON"; + private static final String EXTRA_FACILITATOR_NAME = + "com.android.server.companion.datatransfer.contextsync.extra.FACILITATOR_NAME"; + private static final String EXTRA_FACILITATOR_ID = + "com.android.server.companion.datatransfer.contextsync.extra.FACILITATOR_ID"; + private static final String EXTRA_STATUS = + "com.android.server.companion.datatransfer.contextsync.extra.STATUS"; + private static final String EXTRA_DIRECTION = + "com.android.server.companion.datatransfer.contextsync.extra.DIRECTION"; + private static final String EXTRA_CONTROLS = + "com.android.server.companion.datatransfer.contextsync.extra.CONTROLS"; private String mId; private String mCallerId; private byte[] mAppIcon; private CallFacilitator mFacilitator; private int mStatus; + private int mDirection; private final Set<Integer> mControls = new HashSet<>(); - public static Call fromParcel(Parcel parcel) { + public static Call fromBundle(Bundle bundle) { final Call call = new Call(); - call.setId(parcel.readString()); - call.setCallerId(parcel.readString()); - call.setAppIcon(parcel.readBlob()); - call.setFacilitator(parcel.readParcelable(CallFacilitator.class.getClassLoader(), - CallFacilitator.class)); - call.setStatus(parcel.readInt()); - final int numberOfControls = parcel.readInt(); - for (int i = 0; i < numberOfControls; i++) { - call.addControl(parcel.readInt()); + if (bundle != null) { + call.setId(bundle.getString(CrossDeviceSyncController.EXTRA_CALL_ID)); + call.setCallerId(bundle.getString(EXTRA_CALLER_ID)); + call.setAppIcon(bundle.getByteArray(EXTRA_APP_ICON)); + final String facilitatorName = bundle.getString(EXTRA_FACILITATOR_NAME); + final String facilitatorIdentifier = bundle.getString(EXTRA_FACILITATOR_ID); + call.setFacilitator(new CallFacilitator(facilitatorName, facilitatorIdentifier)); + call.setStatus(bundle.getInt(EXTRA_STATUS)); + call.setDirection(bundle.getInt(EXTRA_DIRECTION)); + call.setControls(new HashSet<>(bundle.getIntegerArrayList(EXTRA_CONTROLS))); } return call; } - @Override - public void writeToParcel(Parcel parcel, int parcelableFlags) { - parcel.writeString(mId); - parcel.writeString(mCallerId); - parcel.writeBlob(mAppIcon); - parcel.writeParcelable(mFacilitator, parcelableFlags); - parcel.writeInt(mStatus); - parcel.writeInt(mControls.size()); - for (int control : mControls) { - parcel.writeInt(control); - } + public Bundle writeToBundle() { + final Bundle bundle = new Bundle(); + bundle.putString(CrossDeviceSyncController.EXTRA_CALL_ID, mId); + bundle.putString(EXTRA_CALLER_ID, mCallerId); + bundle.putByteArray(EXTRA_APP_ICON, mAppIcon); + bundle.putString(EXTRA_FACILITATOR_NAME, mFacilitator.getName()); + bundle.putString(EXTRA_FACILITATOR_ID, mFacilitator.getIdentifier()); + bundle.putInt(EXTRA_STATUS, mStatus); + bundle.putInt(EXTRA_DIRECTION, mDirection); + bundle.putIntegerArrayList(EXTRA_CONTROLS, new ArrayList<>(mControls)); + return bundle; } void setId(String id) { @@ -239,6 +233,10 @@ class CallMetadataSyncData { mStatus = status; } + void setDirection(int direction) { + mDirection = direction; + } + void addControl(int control) { mControls.add(control); } @@ -268,6 +266,10 @@ class CallMetadataSyncData { return mStatus; } + int getDirection() { + return mDirection; + } + Set<Integer> getControls() { return mControls; } @@ -288,23 +290,5 @@ class CallMetadataSyncData { public int hashCode() { return Objects.hashCode(mId); } - - @Override - public int describeContents() { - return 0; - } - - @NonNull public static final Parcelable.Creator<Call> CREATOR = new Parcelable.Creator<>() { - - @Override - public Call createFromParcel(Parcel source) { - return Call.fromParcel(source); - } - - @Override - public Call[] newArray(int size) { - return new Call[size]; - } - }; } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java index b46d5d3eab54..e6d36a543bf6 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncInCallService.java @@ -79,15 +79,20 @@ public class CallMetadataSyncInCallService extends InCallService { int callControlAction) { final CrossDeviceCall crossDeviceCall = getCallForId(crossDeviceCallId, mCurrentCalls.values()); - if (crossDeviceCall == null) { - return; - } switch (callControlAction) { case android.companion.Telecom.ACCEPT: - crossDeviceCall.doAccept(); + if (crossDeviceCall != null) { + crossDeviceCall.doAccept(); + } else { + Slog.w(TAG, "Failed to process accept action; no matching call"); + } break; case android.companion.Telecom.REJECT: - crossDeviceCall.doReject(); + if (crossDeviceCall != null) { + crossDeviceCall.doReject(); + } else { + Slog.w(TAG, "Failed to process reject action; no matching call"); + } break; case android.companion.Telecom.SILENCE: doSilence(); @@ -99,13 +104,25 @@ public class CallMetadataSyncInCallService extends InCallService { doUnmute(); break; case android.companion.Telecom.END: - crossDeviceCall.doEnd(); + if (crossDeviceCall != null) { + crossDeviceCall.doEnd(); + } else { + Slog.w(TAG, "Failed to process end action; no matching call"); + } break; case android.companion.Telecom.PUT_ON_HOLD: - crossDeviceCall.doPutOnHold(); + if (crossDeviceCall != null) { + crossDeviceCall.doPutOnHold(); + } else { + Slog.w(TAG, "Failed to process hold action; no matching call"); + } break; case android.companion.Telecom.TAKE_OFF_HOLD: - crossDeviceCall.doTakeOffHold(); + if (crossDeviceCall != null) { + crossDeviceCall.doTakeOffHold(); + } else { + Slog.w(TAG, "Failed to process unhold action; no matching call"); + } break; default: } @@ -188,6 +205,8 @@ public class CallMetadataSyncInCallService extends InCallService { && mNumberOfActiveSyncAssociations > 0) { mCurrentCalls.remove(call); call.unregisterCallback(mTelecomCallback); + mCdmsi.removeSelfOwnedCallId(call.getDetails().getExtras().getString( + CrossDeviceSyncController.EXTRA_CALL_ID)); sync(getUserId()); } } @@ -196,8 +215,9 @@ public class CallMetadataSyncInCallService extends InCallService { public void onMuteStateChanged(boolean isMuted) { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) && mNumberOfActiveSyncAssociations > 0) { - mCurrentCalls.values().forEach(call -> call.updateMuted(isMuted)); - sync(getUserId()); + mCdmsi.sendCrossDeviceSyncMessageToAllDevices(getUserId(), + CrossDeviceSyncController.createCallControlMessage(null, isMuted + ? android.companion.Telecom.MUTE : android.companion.Telecom.UNMUTE)); } } @@ -205,8 +225,9 @@ public class CallMetadataSyncInCallService extends InCallService { public void onSilenceRinger() { if (CompanionDeviceConfig.isEnabled(CompanionDeviceConfig.ENABLE_CONTEXT_SYNC_TELECOM) && mNumberOfActiveSyncAssociations > 0) { - mCurrentCalls.values().forEach(call -> call.updateSilencedIfRinging()); - sync(getUserId()); + mCdmsi.sendCrossDeviceSyncMessageToAllDevices(getUserId(), + CrossDeviceSyncController.createCallControlMessage(null, + android.companion.Telecom.SILENCE)); } } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java index fec6923e4d06..e8392d29365e 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceCall.java @@ -25,8 +25,10 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.telecom.Call; import android.telecom.CallAudioState; +import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telecom.VideoProfile; +import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -39,9 +41,11 @@ import java.util.UUID; public class CrossDeviceCall { private static final String TAG = "CrossDeviceCall"; + private static final String SEPARATOR = "::"; private final String mId; private final Call mCall; + private final int mUserId; @VisibleForTesting boolean mIsEnterprise; private final String mCallingAppPackageName; private String mCallingAppName; @@ -52,6 +56,7 @@ public class CrossDeviceCall { private String mContactDisplayName; private Uri mHandle; private int mHandlePresentation; + private int mDirection; private boolean mIsMuted; private final Set<Integer> mControls = new HashSet<>(); private final boolean mIsCallPlacedByContextSync; @@ -73,22 +78,24 @@ public class CrossDeviceCall { ? callDetails.getIntentExtras().getString(CrossDeviceSyncController.EXTRA_CALL_ID) : null; final String generatedId = UUID.randomUUID().toString(); - mId = predefinedId != null ? (generatedId + predefinedId) : generatedId; + mId = predefinedId != null ? (generatedId + SEPARATOR + predefinedId) : generatedId; if (call != null) { call.putExtra(CrossDeviceSyncController.EXTRA_CALL_ID, mId); } - mIsCallPlacedByContextSync = - new ComponentName(context, CallMetadataSyncConnectionService.class) - .equals(callDetails.getAccountHandle().getComponentName()); - mCallingAppPackageName = - callDetails.getAccountHandle().getComponentName().getPackageName(); + final PhoneAccountHandle handle = callDetails.getAccountHandle(); + mUserId = handle != null ? handle.getUserHandle().getIdentifier() : -1; + mIsCallPlacedByContextSync = handle != null + && new ComponentName(context, CallMetadataSyncConnectionService.class) + .equals(handle.getComponentName()); + mCallingAppPackageName = handle != null + ? callDetails.getAccountHandle().getComponentName().getPackageName() : ""; mIsEnterprise = (callDetails.getCallProperties() & Call.Details.PROPERTY_ENTERPRISE_CALL) == Call.Details.PROPERTY_ENTERPRISE_CALL; final PackageManager packageManager = context.getPackageManager(); try { final ApplicationInfo applicationInfo = packageManager - .getApplicationInfo(mCallingAppPackageName, - PackageManager.ApplicationInfoFlags.of(0)); + .getApplicationInfoAsUser(mCallingAppPackageName, + PackageManager.ApplicationInfoFlags.of(0), mUserId); mCallingAppName = packageManager.getApplicationLabel(applicationInfo).toString(); mCallingAppIcon = BitmapUtils.renderDrawableToByteArray( packageManager.getApplicationIcon(applicationInfo)); @@ -128,8 +135,19 @@ public class CrossDeviceCall { mContactDisplayName = callDetails.getContactDisplayName(); mHandle = callDetails.getHandle(); mHandlePresentation = callDetails.getHandlePresentation(); + final int direction = callDetails.getCallDirection(); + if (direction == Call.Details.DIRECTION_INCOMING) { + mDirection = android.companion.Telecom.Call.INCOMING; + } else if (direction == Call.Details.DIRECTION_OUTGOING) { + mDirection = android.companion.Telecom.Call.OUTGOING; + } else { + mDirection = android.companion.Telecom.Call.UNKNOWN_DIRECTION; + } mStatus = convertStateToStatus(callDetails.getState()); mControls.clear(); + if (mStatus == android.companion.Telecom.Call.DIALING) { + mControls.add(android.companion.Telecom.END); + } if (mStatus == android.companion.Telecom.Call.RINGING || mStatus == android.companion.Telecom.Call.RINGING_SILENCED) { mControls.add(android.companion.Telecom.ACCEPT); @@ -170,6 +188,8 @@ public class CrossDeviceCall { return android.companion.Telecom.Call.RINGING_SIMULATED; case Call.STATE_DISCONNECTED: return android.companion.Telecom.Call.DISCONNECTED; + case Call.STATE_DIALING: + return android.companion.Telecom.Call.DIALING; default: Slog.e(TAG, "Couldn't resolve state to status: " + callState); return android.companion.Telecom.Call.UNKNOWN_STATUS; @@ -195,6 +215,8 @@ public class CrossDeviceCall { return Call.STATE_SIMULATED_RINGING; case android.companion.Telecom.Call.DISCONNECTED: return Call.STATE_DISCONNECTED; + case android.companion.Telecom.Call.DIALING: + return Call.STATE_DIALING; case android.companion.Telecom.Call.UNKNOWN_STATUS: default: return Call.STATE_NEW; @@ -209,6 +231,10 @@ public class CrossDeviceCall { return mCall; } + public int getUserId() { + return mUserId; + } + public String getCallingAppName() { return mCallingAppName; } @@ -231,11 +257,11 @@ public class CrossDeviceCall { // Cannot use any contact information. return getNonContactString(); } - return mContactDisplayName != null ? mContactDisplayName : getNonContactString(); + return TextUtils.isEmpty(mContactDisplayName) ? getNonContactString() : mContactDisplayName; } private String getNonContactString() { - if (mCallerDisplayName != null + if (!TextUtils.isEmpty(mCallerDisplayName) && mCallerDisplayNamePresentation == TelecomManager.PRESENTATION_ALLOWED) { return mCallerDisplayName; } @@ -250,6 +276,10 @@ public class CrossDeviceCall { return mStatus; } + public int getDirection() { + return mDirection; + } + public Set<Integer> getControls() { return mControls; } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java index bf82f3f4a352..31694590a930 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncController.java @@ -154,6 +154,7 @@ public class CrossDeviceSyncController { Slog.w(TAG, "No callback to report removed transport"); } } + clearInProgressCalls(associationInfo.getId()); } else { // Stable association! final boolean systemBlocked = isAssociationBlocked(associationInfo); @@ -187,6 +188,7 @@ public class CrossDeviceSyncController { // will get stale) syncMessageToDevice(associationInfo.getId(), createEmptyMessage()); + clearInProgressCalls(associationInfo.getId()); } } } @@ -201,9 +203,14 @@ public class CrossDeviceSyncController { return; } final CallMetadataSyncData processedData = processTelecomDataFromSync(data); - mPhoneAccountManager.updateFacilitators(associationId, processedData); - mCallManager.updateCalls(associationId, processedData); - processCallCreateRequests(processedData); + final boolean isRequest = processedData.getCallControlRequests().size() != 0 + || processedData.getCallCreateRequests().size() != 0; + if (!isRequest) { + mPhoneAccountManager.updateFacilitators(associationId, processedData); + mCallManager.updateCalls(associationId, processedData); + } else { + processCallCreateRequests(processedData); + } if (mInCallServiceCallbackRef == null && mConnectionServiceCallbackRef == null) { Slog.w(TAG, "No callback to process context sync message"); @@ -213,8 +220,10 @@ public class CrossDeviceSyncController { mInCallServiceCallbackRef != null ? mInCallServiceCallbackRef.get() : null; if (inCallServiceCallback != null) { - inCallServiceCallback.processContextSyncMessage(associationId, - processedData); + if (isRequest) { + inCallServiceCallback.processContextSyncMessage(associationId, + processedData); + } } else { // This is dead; get rid of it lazily mInCallServiceCallbackRef = null; @@ -224,8 +233,10 @@ public class CrossDeviceSyncController { mConnectionServiceCallbackRef != null ? mConnectionServiceCallbackRef.get() : null; if (connectionServiceCallback != null) { - connectionServiceCallback.processContextSyncMessage(associationId, - processedData); + if (!isRequest) { + connectionServiceCallback.processContextSyncMessage(associationId, + processedData); + } } else { // This is dead; get rid of it lazily mConnectionServiceCallbackRef = null; @@ -236,6 +247,15 @@ public class CrossDeviceSyncController { mCallManager = new CallManager(mContext, mPhoneAccountManager); } + private void clearInProgressCalls(int associationId) { + final Set<String> removedIds = mCallManager.clearCallIdsForAssociationId(associationId); + final CrossDeviceSyncControllerCallback connectionServiceCallback = + mConnectionServiceCallbackRef != null ? mConnectionServiceCallbackRef.get() : null; + if (connectionServiceCallback != null) { + connectionServiceCallback.cleanUpCallIds(removedIds); + } + } + private static boolean isAssociationBlocked(AssociationInfo info) { return (info.getSystemDataSyncFlags() & CompanionDeviceManager.FLAG_CALL_METADATA) != CompanionDeviceManager.FLAG_CALL_METADATA; @@ -274,6 +294,7 @@ public class CrossDeviceSyncController { if (FACILITATOR_ID_SYSTEM.equals(request.getFacilitator().getIdentifier())) { if (request.getAddress() != null && request.getAddress().startsWith( PhoneAccount.SCHEME_TEL)) { + mCallManager.addSelfOwnedCallId(request.getId()); // Remove all the non-numbers (dashes, parens, scheme) final Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, request.getAddress().replaceAll("\\D+", ""), /* fragment= */ null); @@ -382,6 +403,38 @@ public class CrossDeviceSyncController { new int[]{associationId}); } + /** Sync message to all associated devices. */ + public void syncMessageToAllDevicesForUserId(int userId, byte[] message) { + final Set<Integer> associationIds = new HashSet<>(); + for (AssociationInfo associationInfo : mConnectedAssociations) { + if (associationInfo.getUserId() == userId && !isAssociationBlocked(associationInfo)) { + associationIds.add(associationInfo.getId()); + } + } + if (associationIds.isEmpty()) { + Slog.w(TAG, "No eligible devices to sync to"); + return; + } + + mCompanionTransportManager.sendMessage(MESSAGE_REQUEST_CONTEXT_SYNC, message, + associationIds.stream().mapToInt(Integer::intValue).toArray()); + } + + /** + * Mark a call id as owned (i.e. this device owns the canonical call). Note that both sides will + * own outgoing calls that were placed on behalf of another device. + */ + public void addSelfOwnedCallId(String callId) { + mCallManager.addSelfOwnedCallId(callId); + } + + /** Unmark a call id as owned (i.e. this device no longer owns the canonical call). */ + public void removeSelfOwnedCallId(String callId) { + if (callId != null) { + mCallManager.removeSelfOwnedCallId(callId); + } + } + @VisibleForTesting CallMetadataSyncData processTelecomDataFromSync(byte[] data) { final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); @@ -430,8 +483,10 @@ public class CrossDeviceSyncController { pis.end(requestsToken); } else if (pis.getFieldNumber() == (int) Telecom.FACILITATORS) { final long facilitatorsToken = pis.start(Telecom.FACILITATORS); - callMetadataSyncData.addFacilitator( - processFacilitatorDataFromSync(pis)); + final CallMetadataSyncData.CallFacilitator facilitator = + processFacilitatorDataFromSync(pis); + facilitator.setIsTel(true); + callMetadataSyncData.addFacilitator(facilitator); pis.end(facilitatorsToken); } else { Slog.e(TAG, "Unhandled field in Telecom:" @@ -561,6 +616,9 @@ public class CrossDeviceSyncController { case (int) Telecom.Call.STATUS: call.setStatus(pis.readInt(Telecom.Call.STATUS)); break; + case (int) Telecom.Call.DIRECTION: + call.setDirection(pis.readInt(Telecom.Call.DIRECTION)); + break; case (int) Telecom.Call.CONTROLS: call.addControl(pis.readInt(Telecom.Call.CONTROLS)); break; @@ -578,15 +636,15 @@ public class CrossDeviceSyncController { pos.write(ContextSyncMessage.VERSION, CURRENT_VERSION); final long telecomToken = pos.start(ContextSyncMessage.TELECOM); for (CrossDeviceCall call : calls) { - if (call.isCallPlacedByContextSync()) { - // Do not sync any calls which our "ours" as that would be duplicative. + if (call.isCallPlacedByContextSync() || mCallManager.isExternallyOwned(call.getId())) { + // Do not sync any of "our" calls, nor external calls, as that would be duplicative. continue; } final long callsToken = pos.start(Telecom.CALLS); pos.write(Telecom.Call.ID, call.getId()); final long originToken = pos.start(Telecom.Call.ORIGIN); pos.write(Telecom.Call.Origin.CALLER_ID, - call.getReadableCallerId(isAdminBlocked(userId))); + call.getReadableCallerId(isAdminBlocked(call.getUserId()))); pos.write(Telecom.Call.Origin.APP_ICON, call.getCallingAppIcon()); final long facilitatorToken = pos.start(Telecom.Call.Origin.FACILITATOR); pos.write(Telecom.CallFacilitator.NAME, call.getCallingAppName()); @@ -594,6 +652,7 @@ public class CrossDeviceSyncController { pos.end(facilitatorToken); pos.end(originToken); pos.write(Telecom.Call.STATUS, call.getStatus()); + pos.write(Telecom.Call.DIRECTION, call.getDirection()); for (int control : call.getControls()) { pos.write(Telecom.Call.CONTROLS, control); } @@ -658,6 +717,9 @@ public class CrossDeviceSyncController { @VisibleForTesting static class CallManager { + @VisibleForTesting final Set<String> mSelfOwnedCalls = new HashSet<>(); + @VisibleForTesting final Set<String> mExternallyOwnedCalls = new HashSet<>(); + @VisibleForTesting final Map<Integer, Set<String>> mCallIds = new HashMap<>(); private final TelecomManager mTelecomManager; private final PhoneAccountManager mPhoneAccountManager; @@ -678,20 +740,63 @@ public class CrossDeviceSyncController { for (CallMetadataSyncData.Call currentCall : data.getCalls()) { if (!oldCallIds.contains(currentCall.getId()) - && currentCall.getFacilitator() != null) { + && currentCall.getFacilitator() != null + && !isSelfOwned(currentCall.getId())) { + mExternallyOwnedCalls.add(currentCall.getId()); final Bundle extras = new Bundle(); extras.putInt(EXTRA_ASSOCIATION_ID, associationId); extras.putBoolean(EXTRA_IS_REMOTE_ORIGIN, true); - extras.putParcelable(EXTRA_CALL, currentCall); + extras.putBundle(EXTRA_CALL, currentCall.writeToBundle()); extras.putString(EXTRA_CALL_ID, currentCall.getId()); extras.putByteArray(EXTRA_FACILITATOR_ICON, currentCall.getAppIcon()); - final PhoneAccountHandle handle = mPhoneAccountManager.getPhoneAccountHandle( - associationId, currentCall.getFacilitator().getIdentifier()); - mTelecomManager.addNewIncomingCall(handle, extras); + final PhoneAccountHandle handle = + mPhoneAccountManager.getPhoneAccountHandle( + associationId, + currentCall.getFacilitator().getIdentifier()); + if (currentCall.getDirection() == android.companion.Telecom.Call.INCOMING) { + mTelecomManager.addNewIncomingCall(handle, extras); + } else if (currentCall.getDirection() + == android.companion.Telecom.Call.OUTGOING) { + final Bundle wrappedExtras = new Bundle(); + wrappedExtras.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, + extras); + wrappedExtras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, + handle); + final String address = currentCall.getCallerId(); + if (address != null) { + mTelecomManager.placeCall(Uri.fromParts(PhoneAccount.SCHEME_SIP, + address, /* fragment= */ null), wrappedExtras); + } + } } } mCallIds.put(associationId, newCallIds); } + + Set<String> clearCallIdsForAssociationId(int associationId) { + return mCallIds.remove(associationId); + } + + void addSelfOwnedCallId(String callId) { + mSelfOwnedCalls.add(callId); + } + + void removeSelfOwnedCallId(String callId) { + mSelfOwnedCalls.remove(callId); + } + + boolean isExternallyOwned(String callId) { + return mExternallyOwnedCalls.contains(callId); + } + + private boolean isSelfOwned(String currentCallId) { + for (String selfOwnedCallId : mSelfOwnedCalls) { + if (currentCallId.endsWith(selfOwnedCallId)) { + return true; + } + } + return false; + } } static class PhoneAccountManager { @@ -745,7 +850,8 @@ public class CrossDeviceSyncController { new PhoneAccountHandleIdentifier(associationId, facilitator.getIdentifier()); if (!mPhoneAccountHandles.containsKey(phoneAccountHandleIdentifier)) { - registerPhoneAccount(phoneAccountHandleIdentifier, facilitator.getName()); + registerPhoneAccount(phoneAccountHandleIdentifier, facilitator.getName(), + facilitator.isTel()); } } } @@ -755,7 +861,7 @@ public class CrossDeviceSyncController { * synced device, and records it in the local {@link #mPhoneAccountHandles} map. */ private void registerPhoneAccount(PhoneAccountHandleIdentifier handleIdentifier, - String humanReadableAppName) { + String humanReadableAppName, boolean isTel) { if (mPhoneAccountHandles.containsKey(handleIdentifier)) { // Already exists! return; @@ -765,7 +871,8 @@ public class CrossDeviceSyncController { UUID.randomUUID().toString()); mPhoneAccountHandles.put(handleIdentifier, handle); final PhoneAccount phoneAccount = createPhoneAccount(handle, humanReadableAppName, - handleIdentifier.getAppIdentifier()); + handleIdentifier.getAppIdentifier(), handleIdentifier.getAssociationId(), + isTel); mTelecomManager.registerPhoneAccount(phoneAccount); mTelecomManager.enablePhoneAccount(mPhoneAccountHandles.get(handleIdentifier), true); } @@ -781,11 +888,16 @@ public class CrossDeviceSyncController { @VisibleForTesting static PhoneAccount createPhoneAccount(PhoneAccountHandle handle, String humanReadableAppName, - String appIdentifier) { + String appIdentifier, + int associationId, + boolean isTel) { final Bundle extras = new Bundle(); extras.putString(EXTRA_CALL_FACILITATOR_ID, appIdentifier); + extras.putInt(EXTRA_ASSOCIATION_ID, associationId); return new PhoneAccount.Builder(handle, humanReadableAppName) .setExtras(extras) + .setSupportedUriSchemes(List.of(isTel ? PhoneAccount.SCHEME_TEL + : PhoneAccount.SCHEME_SIP)) .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_CONNECTION_MANAGER).build(); } diff --git a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java index 8a0ba27b7526..6764830b830b 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java +++ b/services/companion/java/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerCallback.java @@ -21,6 +21,7 @@ import android.companion.AssociationInfo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Set; /** Callback for call metadata syncing. */ public abstract class CrossDeviceSyncControllerCallback { @@ -40,4 +41,7 @@ public abstract class CrossDeviceSyncControllerCallback { void requestCrossDeviceSync(AssociationInfo associationInfo) {} void updateNumberOfActiveSyncAssociations(int userId, boolean added) {} + + /** Clean up any remaining state for the given calls. */ + void cleanUpCallIds(Set<String> callIds) {} } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 6b55d7ed4d56..cd2f844294e3 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -987,8 +987,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub "Virtual device doesn't have a virtual display with ID " + displayId); } - releaseOwnedVirtualDisplayResources(virtualDisplayWrapper); - + final long ident = Binder.clearCallingIdentity(); + try { + releaseOwnedVirtualDisplayResources(virtualDisplayWrapper); + } finally { + Binder.restoreCallingIdentity(ident); + } } /** diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index e8c85ce68f22..c6e9a7d5e2e5 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -162,6 +162,8 @@ import java.util.concurrent.TimeUnit; // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); + @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN"; + private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); private static final int LOCAL_LOG_LINE_COUNT = 512; @@ -223,7 +225,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { @VisibleForTesting(visibility = Visibility.PRIVATE) VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { - mContext = requireNonNull(context, "Missing context"); + mContext = + requireNonNull(context, "Missing context") + .createAttributionContext(CONTEXT_ATTRIBUTION_TAG); mDeps = requireNonNull(deps, "Missing dependencies"); mLooper = mDeps.getLooper(); @@ -1065,13 +1069,20 @@ public class VcnManagementService extends IVcnManagementService.Stub { boolean isRestricted = false; synchronized (mLock) { final Vcn vcn = mVcns.get(subGrp); + final VcnConfig vcnConfig = mConfigs.get(subGrp); if (vcn != null) { + if (vcnConfig == null) { + // TODO: b/284381334 Investigate for the root cause of this issue + // and handle it properly + logWtf("Vcn instance exists but VcnConfig does not for " + subGrp); + } + if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) { isVcnManagedNetwork = true; } final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports( - subGrp, mLastSnapshot, mConfigs.get(subGrp)); + subGrp, mLastSnapshot, vcnConfig); for (int restrictedTransport : restrictedTransports) { if (ncCopy.hasTransport(restrictedTransport)) { if (restrictedTransport == TRANSPORT_CELLULAR diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java index 2b43ef48b922..0d423d8a0a62 100644 --- a/services/core/java/com/android/server/VpnManagerService.java +++ b/services/core/java/com/android/server/VpnManagerService.java @@ -80,6 +80,7 @@ import java.util.List; */ public class VpnManagerService extends IVpnManager.Stub { private static final String TAG = VpnManagerService.class.getSimpleName(); + private static final String CONTEXT_ATTRIBUTION_TAG = "VPN_MANAGER"; @VisibleForTesting protected final HandlerThread mHandlerThread; @@ -157,7 +158,7 @@ public class VpnManagerService extends IVpnManager.Stub { } public VpnManagerService(Context context, Dependencies deps) { - mContext = context; + mContext = context.createAttributionContext(CONTEXT_ATTRIBUTION_TAG); mDeps = deps; mHandlerThread = mDeps.makeHandlerThread(); mHandlerThread.start(); @@ -406,7 +407,7 @@ public class VpnManagerService extends IVpnManager.Stub { * Retrieve the VpnProfileState for the profile provisioned by the given package. * * @return the VpnProfileState with current information, or null if there was no profile - * provisioned by the given package. + * provisioned and started by the given package. * @hide */ @Override diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java index 2812233815a6..99178920cc52 100644 --- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java +++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java @@ -88,7 +88,7 @@ public class WallpaperUpdateReceiver extends BroadcastReceiver { } else { //live wallpaper ComponentName currCN = info.getComponent(); - ComponentName defaultCN = WallpaperManager.getCmfDefaultWallpaperComponent(context); + ComponentName defaultCN = WallpaperManager.getDefaultWallpaperComponent(context); if (!currCN.equals(defaultCN)) { return true; } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 00dbb97191fb..fc84e1386d1f 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -70,6 +70,7 @@ import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMI import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED; import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN; import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN; +import static android.os.PowerExemptionManager.REASON_OTHER; import static android.os.PowerExemptionManager.REASON_PACKAGE_INSTALLER; import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT; import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI; @@ -85,7 +86,6 @@ import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE; import static android.os.PowerExemptionManager.REASON_SYSTEM_UID; import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE; import static android.os.PowerExemptionManager.REASON_UID_VISIBLE; -import static android.os.PowerExemptionManager.REASON_UNKNOWN; import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.PowerExemptionManager.getReasonCodeFromProcState; import static android.os.PowerExemptionManager.reasonCodeToString; @@ -621,7 +621,8 @@ public final class ActiveServices { try { final ServiceRecord.StartItem si = r.pendingStarts.get(0); startServiceInnerLocked(this, si.intent, r, false, true, si.callingId, - si.mCallingProcessName, r.startRequested, si.mCallingPackageName); + si.mCallingProcessName, si.mCallingProcessState, + r.startRequested, si.mCallingPackageName); } catch (TransactionTooLargeException e) { // Ignore, nobody upstack cares. } @@ -977,10 +978,22 @@ public final class ActiveServices { fgRequired = false; } + final ProcessRecord callingApp; + synchronized (mAm.mPidsSelfLocked) { + callingApp = mAm.mPidsSelfLocked.get(callingPid); + } + final String callingProcessName = callingApp != null + ? callingApp.processName : callingPackage; + final int callingProcessState = + callingApp != null && callingApp.getThread() != null && !callingApp.isKilled() + ? callingApp.mState.getCurProcState() : ActivityManager.PROCESS_STATE_UNKNOWN; + r.updateProcessStateOnRequest(); + // The package could be frozen (meaning it's doing surgery), defer the actual // start until the package is unfrozen. if (deferServiceBringupIfFrozenLocked(r, service, callingPackage, callingFeatureId, - callingUid, callingPid, fgRequired, callerFg, userId, + callingUid, callingPid, callingProcessName, + callingProcessState, fgRequired, callerFg, userId, backgroundStartPrivileges, false, null)) { return null; } @@ -1001,7 +1014,7 @@ public final class ActiveServices { // what realResult contains. final ComponentName realResult = startServiceInnerLocked(r, service, callingUid, callingPid, - getCallingProcessNameLocked(callingUid, callingPid, callingPackage), + callingProcessName, callingProcessState, fgRequired, callerFg, backgroundStartPrivileges, callingPackage); if (res.aliasComponent != null @@ -1013,17 +1026,9 @@ public final class ActiveServices { } } - private String getCallingProcessNameLocked(int callingUid, int callingPid, - String callingPackage) { - synchronized (mAm.mPidsSelfLocked) { - final ProcessRecord callingApp = mAm.mPidsSelfLocked.get(callingPid); - return callingApp != null ? callingApp.processName : callingPackage; - } - } - private ComponentName startServiceInnerLocked(ServiceRecord r, Intent service, - int callingUid, int callingPid, String callingProcessName, boolean fgRequired, - boolean callerFg, + int callingUid, int callingPid, String callingProcessName, + int callingProcessState, boolean fgRequired, boolean callerFg, BackgroundStartPrivileges backgroundStartPrivileges, String callingPackage) throws TransactionTooLargeException { NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent( @@ -1037,7 +1042,8 @@ public final class ActiveServices { r.delayedStop = false; r.fgRequired = fgRequired; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - service, neededGrants, callingUid, callingProcessName, callingPackage)); + service, neededGrants, callingUid, callingProcessName, callingPackage, + callingProcessState)); // We want to allow scheduling user-initiated jobs when the app is running a // foreground service that was started in the same conditions that allows for scheduling @@ -1140,7 +1146,8 @@ public final class ActiveServices { r.allowBgActivityStartsOnServiceStart(backgroundStartPrivileges); } ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting, - callingUid, callingProcessName, wasStartRequested, callingPackage); + callingUid, callingProcessName, callingProcessState, + wasStartRequested, callingPackage); return cmp; } @@ -1244,7 +1251,8 @@ public final class ActiveServices { @GuardedBy("mAm") private boolean deferServiceBringupIfFrozenLocked(ServiceRecord s, Intent serviceIntent, String callingPackage, @Nullable String callingFeatureId, - int callingUid, int callingPid, boolean fgRequired, boolean callerFg, int userId, + int callingUid, int callingPid, String callingProcessName, + int callingProcessState, boolean fgRequired, boolean callerFg, int userId, BackgroundStartPrivileges backgroundStartPrivileges, boolean isBinding, IServiceConnection connection) { final PackageManagerInternal pm = mAm.getPackageManagerInternal(); @@ -1258,8 +1266,6 @@ public final class ActiveServices { curPendingBringups = new ArrayList<>(); mPendingBringups.put(s, curPendingBringups); } - final String callingProcessName = getCallingProcessNameLocked( - callingUid, callingPid, callingPackage); curPendingBringups.add(new Runnable() { @Override public void run() { @@ -1291,7 +1297,7 @@ public final class ActiveServices { } else { // Starting a service try { startServiceInnerLocked(s, serviceIntent, callingUid, callingPid, - callingProcessName, fgRequired, callerFg, + callingProcessName, callingProcessState, fgRequired, callerFg, backgroundStartPrivileges, callingPackage); } catch (TransactionTooLargeException e) { /* ignore - local call */ @@ -1338,7 +1344,8 @@ public final class ActiveServices { ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting, int callingUid, String callingProcessName, - boolean wasStartRequested, String callingPackage) throws TransactionTooLargeException { + int callingProcessState, boolean wasStartRequested, String callingPackage) + throws TransactionTooLargeException { synchronized (mAm.mProcessStats.mLock) { final ServiceState stracker = r.getTracker(); if (stracker != null) { @@ -1381,7 +1388,9 @@ public final class ActiveServices { getShortServiceNameForStats(r), packageState, packageName, - callingPackage); + callingPackage, + callingProcessState, + r.mProcessStateOnRequest); if (r.startRequested && addToStarting) { boolean first = smap.mStartingBackground.size() == 0; @@ -2156,10 +2165,7 @@ public final class ActiveServices { } } - final boolean enableFgsWhileInUseFix = mAm.mConstants.mEnableFgsWhileInUseFix; - final boolean fgsTypeChangingFromShortFgs = r.isForeground && isOldTypeShortFgs; - - if (fgsTypeChangingFromShortFgs) { + if (r.isForeground && isOldTypeShortFgs) { // If we get here, that means startForeground(SHORT_SERVICE) is called again // on a SHORT_SERVICE FGS. @@ -2212,21 +2218,7 @@ public final class ActiveServices { // "if (r.mAllowStartForeground == REASON_DENIED...)" block below. } } - } - - boolean resetNeededForLogging = false; - - // Re-evaluate mAllowWhileInUsePermissionInFgs and mAllowStartForeground - // (i.e. while-in-use and BFSL flags) if needed. - // - // Consider the below if-else section to be in the else of the above - // `if (fgsTypeChangingFromShortFgs)`. - // Using an else would increase the indent further, so we don't use it here - // and instead just add !fgsTypeChangingFromShortFgs to all if's. - // - // The first if's are for the original while-in-use logic. - if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix - && r.mStartForegroundCount == 0) { + } else if (r.mStartForegroundCount == 0) { /* If the service was started with startService(), not startForegroundService(), and if startForeground() isn't called within @@ -2257,8 +2249,7 @@ public final class ActiveServices { r.mLoggedInfoAllowStartForeground = false; } } - } else if (!fgsTypeChangingFromShortFgs && !enableFgsWhileInUseFix - && r.mStartForegroundCount >= 1) { + } else if (r.mStartForegroundCount >= 1) { // We get here if startForeground() is called multiple times // on the same service after it's created, regardless of whether // stopForeground() has been called or not. @@ -2269,100 +2260,6 @@ public final class ActiveServices { r.appInfo.uid, r.intent.getIntent(), r, r.userId, BackgroundStartPrivileges.NONE, false /* isBindService */, false /* isStartService */); - } else if (!fgsTypeChangingFromShortFgs && enableFgsWhileInUseFix) { - // The new while-in-use logic. - // - // When startForeground() is called, we _always_ call - // setFgsRestrictionLocked() to set the restrictions according to the - // current state of the app. - // (So if the app is now in TOP, for example, the service will now always - // get while-in-use permissions.) - // - // Note, setFgsRestrictionLocked() will never disallow - // mAllowWhileInUsePermissionInFgs nor mAllowStartForeground - // (i.e. while-in-use and BFSL flags) once they're set to "allowed". - // - // HOWEVER, if these flags were set to "allowed" in Context.startService() - // (as opposed to startForegroundService()), when the service wasn't yet - // a foreground service, then we may not always - // want to trust them -- for example, if the service has been running as a - // BG service or a bound service for a long time when the app is not longer - // in the foreground, then we shouldn't grant while-in-user nor BFSL. - // So in that case, we need to reset it first. - - final long delayMs = - (r.mLastUntrustedSetFgsRestrictionAllowedTime == 0) ? 0 - : (SystemClock.elapsedRealtime() - - r.mLastUntrustedSetFgsRestrictionAllowedTime); - final boolean resetNeeded = - !r.isForeground - && delayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs; - if (resetNeeded) { - // We don't want to reset mDebugWhileInUseReasonInBindService here -- - // we'll instead reset it in the following code, using the simulated - // legacy logic. - resetFgsRestrictionLocked(r, - /*resetDebugWhileInUseReasonInBindService=*/ false); - } - - // Simulate the reset flow in the legacy logic to reset - // mDebugWhileInUseReasonInBindService. - // (Which is only used to compare to the old logic.) - final long legacyDelayMs = SystemClock.elapsedRealtime() - r.createRealTime; - if ((r.mStartForegroundCount == 0) - && (legacyDelayMs > mAm.mConstants.mFgsStartForegroundTimeoutMs)) { - r.mDebugWhileInUseReasonInBindService = REASON_DENIED; - } - - setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(), - r.appInfo.uid, r.intent.getIntent(), r, r.userId, - BackgroundStartPrivileges.NONE, - false /* isBindService */, false /* isStartService */); - - final String temp = "startForegroundDelayMs:" + delayMs - + "; started: " + r.startRequested - + "; num_bindings: " + r.getConnections().size() - + "; wasForeground: " + r.isForeground - + "; resetNeeded:" + resetNeeded; - if (r.mInfoAllowStartForeground != null) { - r.mInfoAllowStartForeground += "; " + temp; - } else { - r.mInfoAllowStartForeground = temp; - } - r.mLoggedInfoAllowStartForeground = false; - - resetNeededForLogging = resetNeeded; - } - - // If the service has any bindings and it's not yet a FGS - // we compare the new and old while-in-use logics. - // (If it's not the first startForeground() call, we already reset the - // while-in-use and BFSL flags, so the logic change wouldn't matter.) - // - // Note, mDebugWhileInUseReasonInBindService does *not* fully simulate the - // legacy logic, because we'll only set it in bindService(), but the actual - // mAllowWhileInUsePermissionInFgsReason can change afterwards, in a subsequent - // Service.startForeground(). This check will only provide "rough" check. - // But if mDebugWhileInUseReasonInBindService is _not_ DENIED, and - // mDebugWhileInUseReasonInStartForeground _is_ DENIED, then that means we'd - // now detected a behavior change. - // OTOH, if it's changing from non-DENIED to another non-DENIED, that may - // not be a problem. - if (enableFgsWhileInUseFix - && !r.isForeground - && (r.getConnections().size() > 0) - && (r.mDebugWhileInUseReasonInBindService - != r.mDebugWhileInUseReasonInStartForeground)) { - logWhileInUseChangeWtf("FGS while-in-use changed (b/276963716): old=" - + reasonCodeToString(r.mDebugWhileInUseReasonInBindService) - + " new=" - + reasonCodeToString(r.mDebugWhileInUseReasonInStartForeground) - + " startForegroundCount=" + r.mStartForegroundCount - + " started=" + r.startRequested - + " num_bindings=" + r.getConnections().size() - + " resetNeeded=" + resetNeededForLogging - + " " - + r.shortInstanceName); } // If the foreground service is not started from TOP process, do not allow it to @@ -2471,10 +2368,6 @@ public final class ActiveServices { } r.isForeground = true; - // Once the service becomes a foreground service, - // the FGS restriction information always becomes "trustable". - r.mLastUntrustedSetFgsRestrictionAllowedTime = 0; - // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could // be deferred, make a copy of mAllowStartForeground and // mAllowWhileInUsePermissionInFgs. @@ -2620,13 +2513,6 @@ public final class ActiveServices { } } - /** - * It just does a wtf, but extracted to a method, so we can do a signature search on pitot. - */ - private void logWhileInUseChangeWtf(String message) { - Slog.wtf(TAG, message); - } - private boolean withinFgsDeferRateLimit(ServiceRecord sr, final long now) { // If we're still within the service's deferral period, then by definition // deferral is not rate limited. @@ -3734,11 +3620,22 @@ public final class ActiveServices { return 0; } + final ProcessRecord callingApp; + synchronized (mAm.mPidsSelfLocked) { + callingApp = mAm.mPidsSelfLocked.get(callingPid); + } + final String callingProcessName = callingApp != null + ? callingApp.processName : callingPackage; + final int callingProcessState = + callingApp != null && callingApp.getThread() != null && !callingApp.isKilled() + ? callingApp.mState.getCurProcState() : ActivityManager.PROCESS_STATE_UNKNOWN; + s.updateProcessStateOnRequest(); + // The package could be frozen (meaning it's doing surgery), defer the actual // binding until the package is unfrozen. boolean packageFrozen = deferServiceBringupIfFrozenLocked(s, service, callingPackage, null, - callingUid, callingPid, false, callerFg, userId, BackgroundStartPrivileges.NONE, - true, connection); + callingUid, callingPid, callingProcessName, callingProcessState, + false, callerFg, userId, BackgroundStartPrivileges.NONE, true, connection); // If permissions need a review before any of the app components can run, // we schedule binding to the service but do not start its process, then @@ -3839,25 +3736,9 @@ public final class ActiveServices { return 0; } } - if (!mAm.mConstants.mEnableFgsWhileInUseFix) { - // Old while-in-use logic. - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId, - BackgroundStartPrivileges.NONE, true /* isBindService */, - false /* isStartService */); - } else { - // New logic will not call setFgsRestrictionLocked() here, but we still - // keep track of the allow reason from the old logic here, so we can compare to - // the new logic. - // Once we're confident enough in the new logic, we should remove it. - if (s.mDebugWhileInUseReasonInBindService == REASON_DENIED) { - s.mDebugWhileInUseReasonInBindService = - shouldAllowFgsWhileInUsePermissionLocked( - callingPackage, callingPid, callingUid, s.app, - BackgroundStartPrivileges.NONE, - true /* isBindService */, - false /* DO NOT enableFgsWhileInUseFix; use the old logic */); - } - } + setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId, + BackgroundStartPrivileges.NONE, true /* isBindService */, + false /* isStartService */); if (s.app != null) { ProcessServiceRecord servicePsr = s.app.mServices; @@ -3895,7 +3776,9 @@ public final class ActiveServices { getShortServiceNameForStats(s), packageState, s.packageName, - callerApp.info.packageName); + callerApp.info.packageName, + callerApp.mState.getCurProcState(), + s.mProcessStateOnRequest); if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b + ": received=" + b.intent.received @@ -5494,7 +5377,7 @@ public final class ActiveServices { // be called. if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), - null, null, 0, null, null)); + null, null, 0, null, null, ActivityManager.PROCESS_STATE_UNKNOWN)); } sendServiceArgsLocked(r, execInFg, true); @@ -6490,7 +6373,8 @@ public final class ActiveServices { stopServiceLocked(sr, true); } else { sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, - sr.getLastStartId(), baseIntent, null, 0, null, null)); + sr.getLastStartId(), baseIntent, null, 0, null, null, + ActivityManager.PROCESS_STATE_UNKNOWN)); if (sr.app != null && sr.app.getThread() != null) { // We always run in the foreground, since this is called as // part of the "remove task" UI operation. @@ -7554,57 +7438,38 @@ public final class ActiveServices { * @param r the service to start. * @param isStartService True if it's called from Context.startService(). * False if it's called from Context.startForegroundService() or - * Service.startService(). + * Service.startForeground(). * @return true if allow, false otherwise. */ private void setFgsRestrictionLocked(String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId, BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService, boolean isStartService) { - final long now = SystemClock.elapsedRealtime(); - // Check DeviceConfig flag. if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { + if (!r.mAllowWhileInUsePermissionInFgs) { + // BGFGS start restrictions are disabled. We're allowing while-in-use permissions. + // Note REASON_OTHER since there's no other suitable reason. + r.mAllowWhileInUsePermissionInFgsReason = REASON_OTHER; + } r.mAllowWhileInUsePermissionInFgs = true; } - final @ReasonCode int allowWhileInUse; - - // Either (or both) mAllowWhileInUsePermissionInFgs or mAllowStartForeground is - // newly allowed? - boolean newlyAllowed = false; if (!r.mAllowWhileInUsePermissionInFgs || (r.mAllowStartForeground == REASON_DENIED)) { - allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( - callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges, - isBindService); + @ReasonCode final int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( + callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges); // We store them to compare the old and new while-in-use logics to each other. // (They're not used for any other purposes.) - if (isBindService) { - r.mDebugWhileInUseReasonInBindService = allowWhileInUse; - } else { - r.mDebugWhileInUseReasonInStartForeground = allowWhileInUse; - } if (!r.mAllowWhileInUsePermissionInFgs) { r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED); - newlyAllowed |= r.mAllowWhileInUsePermissionInFgs; + r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse; } if (r.mAllowStartForeground == REASON_DENIED) { r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked( allowWhileInUse, callingPackage, callingPid, callingUid, intent, r, backgroundStartPrivileges, isBindService); - newlyAllowed |= r.mAllowStartForeground != REASON_DENIED; } - } else { - allowWhileInUse = REASON_UNKNOWN; - } - r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse; - - if (isStartService && !r.isForeground && newlyAllowed) { - // If it's called by Context.startService() (not by startForegroundService()), - // and we're setting "allowed", then we can't fully trust it yet -- we'll need to reset - // the restrictions if startForeground() is called after the grace period. - r.mLastUntrustedSetFgsRestrictionAllowedTime = now; } } @@ -7612,28 +7477,12 @@ public final class ActiveServices { * Reset various while-in-use and BFSL related information. */ void resetFgsRestrictionLocked(ServiceRecord r) { - resetFgsRestrictionLocked(r, /*resetDebugWhileInUseReasonInBindService=*/ true); - } - - /** - * Reset various while-in-use and BFSL related information. - */ - void resetFgsRestrictionLocked(ServiceRecord r, - boolean resetDebugWhileInUseReasonInBindService) { r.mAllowWhileInUsePermissionInFgs = false; r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED; - r.mDebugWhileInUseReasonInStartForeground = REASON_DENIED; - - // In Service.startForeground(), we reset this field using a legacy logic, - // so resetting this field is optional. - if (resetDebugWhileInUseReasonInBindService) { - r.mDebugWhileInUseReasonInBindService = REASON_DENIED; - } r.mAllowStartForeground = REASON_DENIED; r.mInfoAllowStartForeground = null; r.mInfoTempFgsAllowListReason = null; r.mLoggedInfoAllowStartForeground = false; - r.mLastUntrustedSetFgsRestrictionAllowedTime = 0; r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs); } @@ -7643,7 +7492,7 @@ public final class ActiveServices { } final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( callingPackage, callingPid, callingUid, null /* targetProcess */, - BackgroundStartPrivileges.NONE, false); + BackgroundStartPrivileges.NONE); @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked( allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */, BackgroundStartPrivileges.NONE); @@ -7667,30 +7516,15 @@ public final class ActiveServices { */ private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, int callingPid, int callingUid, @Nullable ProcessRecord targetProcess, - BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) { - return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, - callingPid, callingUid, targetProcess, backgroundStartPrivileges, isBindService, - /* enableFgsWhileInUseFix =*/ mAm.mConstants.mEnableFgsWhileInUseFix); - } - - private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage, - int callingPid, int callingUid, @Nullable ProcessRecord targetProcess, - BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService, - boolean enableFgsWhileInUseFix) { + BackgroundStartPrivileges backgroundStartPrivileges) { int ret = REASON_DENIED; - // Define some local variables for better readability... - final boolean useOldLogic = !enableFgsWhileInUseFix; - final boolean forStartForeground = !isBindService; - - if (useOldLogic || forStartForeground) { - final int uidState = mAm.getUidStateLocked(callingUid); - if (ret == REASON_DENIED) { - // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT, - // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP. - if (uidState <= PROCESS_STATE_TOP) { - ret = getReasonCodeFromProcState(uidState); - } + final int uidState = mAm.getUidStateLocked(callingUid); + if (ret == REASON_DENIED) { + // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT, + // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP. + if (uidState <= PROCESS_STATE_TOP) { + ret = getReasonCodeFromProcState(uidState); } } @@ -7733,10 +7567,6 @@ public final class ActiveServices { } } - if (enableFgsWhileInUseFix && ret == REASON_DENIED) { - ret = shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid); - } - if (ret == REASON_DENIED) { // Allow FGS while-in-use if the WindowManager allows background activity start. // This is mainly to get the 10 seconds grace period if any activity in the caller has @@ -7915,7 +7745,7 @@ public final class ActiveServices { shouldAllowFgsWhileInUsePermissionLocked( clientPackageName, clientPid, clientUid, null /* targetProcess */, - BackgroundStartPrivileges.NONE, false); + BackgroundStartPrivileges.NONE); final @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked( allowWhileInUse2, @@ -8344,7 +8174,7 @@ public final class ActiveServices { String callingPackage) { return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid, /* targetProcess */ null, - BackgroundStartPrivileges.NONE, false) + BackgroundStartPrivileges.NONE) != REASON_DENIED; } @@ -8352,7 +8182,7 @@ public final class ActiveServices { String callingPackage, @Nullable ProcessRecord targetProcess, @NonNull BackgroundStartPrivileges backgroundStartPrivileges) { return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid, - targetProcess, backgroundStartPrivileges, false) != REASON_DENIED; + targetProcess, backgroundStartPrivileges) != REASON_DENIED; } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 3b446338298d..8c31209aeeb4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -1058,13 +1058,6 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_USE_MODERN_TRIM */ public boolean USE_MODERN_TRIM = DEFAULT_USE_MODERN_TRIM; - private static final String KEY_ENABLE_FGS_WHILE_IN_USE_FIX = - "key_enable_fgs_while_in_use_fix"; - - private static final boolean DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX = false; - - public volatile boolean mEnableFgsWhileInUseFix = DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX; - private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = new OnPropertiesChangedListener() { @Override @@ -1233,9 +1226,6 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION: updateEnableWaitForFinishAttachApplication(); break; - case KEY_ENABLE_FGS_WHILE_IN_USE_FIX: - updateEnableFgsWhileInUseFix(); - break; case KEY_MAX_PREVIOUS_TIME: updateMaxPreviousTime(); break; @@ -1688,9 +1678,9 @@ final class ActivityManagerConstants extends ContentObserver { DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME, DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS); - if (mKillBgRestrictedAndCachedIdleSettleTimeMs != currentSettleTime) { - mService.mHandler.removeMessages( - ActivityManagerService.IDLE_UIDS_MSG); + if (mKillBgRestrictedAndCachedIdleSettleTimeMs < currentSettleTime) { + // Don't remove existing messages in case other IDLE_UIDS_MSG initiators use lower + // delays, but send a new message if the settle time has decreased. mService.mHandler.sendEmptyMessageDelayed( ActivityManagerService.IDLE_UIDS_MSG, mKillBgRestrictedAndCachedIdleSettleTimeMs); @@ -2005,12 +1995,6 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION); } - private void updateEnableFgsWhileInUseFix() { - mEnableFgsWhileInUseFix = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, - KEY_ENABLE_FGS_WHILE_IN_USE_FIX, - DEFAULT_ENABLE_FGS_WHILE_IN_USE_FIX); - } private void updateUseTieredCachedAdj() { USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -2211,9 +2195,6 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" "); pw.print(KEY_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED); pw.print("="); pw.println(mFlagSystemExemptPowerRestrictionsEnabled); - pw.print(" "); pw.print(KEY_ENABLE_FGS_WHILE_IN_USE_FIX); - pw.print("="); pw.println(mEnableFgsWhileInUseFix); - pw.print(" "); pw.print(KEY_SHORT_FGS_TIMEOUT_DURATION); pw.print("="); pw.println(mShortFgsTimeoutDuration); pw.print(" "); pw.print(KEY_SHORT_FGS_PROC_STATE_EXTRA_WAIT_DURATION); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1bd0675e95b6..0916960fae13 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1176,20 +1176,24 @@ public class ActivityManagerService extends IActivityManager.Stub public Intent intent; public boolean deferUntilActive; public int originalCallingUid; + /** The snapshot process state of the app who sent this broadcast */ + public int originalCallingAppProcessState; public static StickyBroadcast create(Intent intent, boolean deferUntilActive, - int originalCallingUid) { + int originalCallingUid, int originalCallingAppProcessState) { final StickyBroadcast b = new StickyBroadcast(); b.intent = intent; b.deferUntilActive = deferUntilActive; b.originalCallingUid = originalCallingUid; + b.originalCallingAppProcessState = originalCallingAppProcessState; return b; } @Override public String toString() { return "{intent=" + intent + ", defer=" + deferUntilActive + ", originalCallingUid=" - + originalCallingUid + "}"; + + originalCallingUid + ", originalCallingAppProcessState=" + + originalCallingAppProcessState + "}"; } } @@ -1489,12 +1493,10 @@ public class ActivityManagerService extends IActivityManager.Stub static final class ProcessChangeItem { static final int CHANGE_ACTIVITIES = 1<<0; static final int CHANGE_FOREGROUND_SERVICES = 1<<1; - static final int CHANGE_CAPABILITY = 1<<2; int changes; int uid; int pid; int processState; - int capability; boolean foregroundActivities; int foregroundServiceTypes; } @@ -1524,6 +1526,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ int mBootPhase; + volatile boolean mDeterministicUidIdle = false; + @VisibleForTesting public WindowManagerService mWindowManager; WindowManagerInternal mWmInternal; @@ -1618,6 +1622,8 @@ public class ActivityManagerService extends IActivityManager.Stub static final int SERVICE_SHORT_FGS_PROCSTATE_TIMEOUT_MSG = 77; static final int SERVICE_SHORT_FGS_ANR_TIMEOUT_MSG = 78; static final int UPDATE_CACHED_APP_HIGH_WATERMARK = 79; + static final int ADD_UID_TO_OBSERVER_MSG = 80; + static final int REMOVE_UID_FROM_OBSERVER_MSG = 81; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1776,6 +1782,12 @@ public class ActivityManagerService extends IActivityManager.Stub case PUSH_TEMP_ALLOWLIST_UI_MSG: { pushTempAllowlist(); } break; + case ADD_UID_TO_OBSERVER_MSG: { + mUidObserverController.addUidToObserverImpl((IBinder) msg.obj, msg.arg1); + } break; + case REMOVE_UID_FROM_OBSERVER_MSG: { + mUidObserverController.removeUidFromObserverImpl((IBinder) msg.obj, msg.arg1); + } break; } } } @@ -13787,7 +13799,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (action.startsWith("android.intent.action.USER_") || action.startsWith("android.intent.action.PACKAGE_") || action.startsWith("android.intent.action.UID_") - || action.startsWith("android.intent.action.EXTERNAL_")) { + || action.startsWith("android.intent.action.EXTERNAL_") + || action.startsWith("android.bluetooth.")) { if (DEBUG_BROADCAST) { Slog.wtf(TAG, "System internals registering for " + filter.toLongString() @@ -14035,7 +14048,8 @@ public class ActivityManagerService extends IActivityManager.Stub receivers, null, null, 0, null, null, false, true, true, -1, originalStickyCallingUid, BackgroundStartPrivileges.NONE, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */, - null /* filterExtrasForReceiver */); + null /* filterExtrasForReceiver */, + broadcast.originalCallingAppProcessState); queue.enqueueBroadcastLocked(r); } } @@ -14850,6 +14864,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } + final int callerAppProcessState = getRealProcessStateLocked(callerApp, realCallingPid); // Add to the sticky list if requested. if (sticky) { if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, @@ -14912,12 +14927,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (intent.filterEquals(list.get(i).intent)) { // This sticky already exists, replace it. list.set(i, StickyBroadcast.create(new Intent(intent), deferUntilActive, - callingUid)); + callingUid, callerAppProcessState)); break; } } if (i >= stickiesCount) { - list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid)); + list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive, callingUid, + callerAppProcessState)); } } @@ -15000,7 +15016,8 @@ public class ActivityManagerService extends IActivityManager.Stub requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, registeredReceivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver); + backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, + callerAppProcessState); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); queue.enqueueBroadcastLocked(r); registeredReceivers = null; @@ -15094,7 +15111,8 @@ public class ActivityManagerService extends IActivityManager.Stub requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions, receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, - backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver); + backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, + callerAppProcessState); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); queue.enqueueBroadcastLocked(r); @@ -15111,6 +15129,19 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManager.BROADCAST_SUCCESS; } + @GuardedBy("this") + private int getRealProcessStateLocked(ProcessRecord app, int pid) { + if (app == null) { + synchronized (mPidsSelfLocked) { + app = mPidsSelfLocked.get(pid); + } + } + if (app != null && app.getThread() != null && !app.isKilled()) { + return app.mState.getCurProcState(); + } + return PROCESS_STATE_NONEXISTENT; + } + @VisibleForTesting ArrayList<StickyBroadcast> getStickyBroadcasts(String action, int userId) { final ArrayMap<String, ArrayList<StickyBroadcast>> stickyBroadcasts = @@ -16465,6 +16496,11 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void setDeterministicUidIdle(boolean deterministic) { + mDeterministicUidIdle = deterministic; + } + /** Make the currently active UIDs idle after a certain grace period. */ final void idleUids() { synchronized (this) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index add22bd6009e..fd980727e12b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -290,6 +290,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runKillAll(pw); case "make-uid-idle": return runMakeIdle(pw); + case "set-deterministic-uid-idle": + return runSetDeterministicUidIdle(pw); case "monitor": return runMonitor(pw); case "watch-uids": @@ -1520,6 +1522,23 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runSetDeterministicUidIdle(PrintWriter pw) throws RemoteException { + int userId = UserHandle.USER_ALL; + + String opt; + while ((opt = getNextOption()) != null) { + if (opt.equals("--user")) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + getErrPrintWriter().println("Error: Unknown option: " + opt); + return -1; + } + } + boolean deterministic = Boolean.parseBoolean(getNextArgRequired()); + mInterface.setDeterministicUidIdle(deterministic); + return 0; + } + static final class MyActivityController extends IActivityController.Stub { final IActivityManager mInterface; final PrintWriter mPw; @@ -4271,6 +4290,11 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" make-uid-idle [--user <USER_ID> | all | current] <PACKAGE>"); pw.println(" If the given application's uid is in the background and waiting to"); pw.println(" become idle (not allowing background services), do that now."); + pw.println( + " set-deterministic-uid-idle [--user <USER_ID> | all | current] <true|false>"); + pw.println(" If true, sets the timing of making UIDs idle consistent and"); + pw.println(" deterministic. If false, the timing will be variable depending on"); + pw.println(" other activity on the device. The default is false."); pw.println(" monitor [--gdb <port>] [-p <TARGET>] [-s] [-c] [-k]"); pw.println(" Start monitoring for crashes or ANRs."); pw.println(" --gdb: start gdbserv on the given port at crash/ANR"); diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 0b5b1cb2902e..a80ad599a3e2 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -25,6 +25,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; +import android.app.BroadcastOptions; import android.content.Intent; import android.content.pm.ResolveInfo; import android.os.SystemClock; @@ -105,6 +106,12 @@ class BroadcastProcessQueue { long lastCpuDelayTime; /** + * Snapshotted value of {@link ProcessStateRecord#getCurProcState()} before + * dispatching the current broadcast to the receiver in this process. + */ + int lastProcessState; + + /** * Ordered collection of broadcasts that are waiting to be dispatched to * this process, as a pair of {@link BroadcastRecord} and the index into * {@link BroadcastRecord#receivers} that represents the receiver. @@ -257,7 +264,10 @@ class BroadcastProcessQueue { deferredStatesApplyConsumer.accept(record, recordIndex); } - if (record.isReplacePending()) { + // Ignore FLAG_RECEIVER_REPLACE_PENDING if the sender specified the policy using the + // BroadcastOptions delivery group APIs. + if (record.isReplacePending() + && record.getDeliveryGroupPolicy() == BroadcastOptions.DELIVERY_GROUP_POLICY_ALL) { final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex); if (replacedBroadcastRecord != null) { return replacedBroadcastRecord; diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index 4eedfe259633..f13dc89f2bd2 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -264,12 +264,15 @@ public class BroadcastQueueImpl extends BroadcastQueue { if (oldRecord.resultTo != null) { try { oldRecord.mIsReceiverAppRunning = true; - performReceiveLocked(oldRecord.resultToApp, oldRecord.resultTo, + performReceiveLocked(oldRecord, oldRecord.resultToApp, oldRecord.resultTo, oldRecord.intent, Activity.RESULT_CANCELED, null, null, false, false, oldRecord.shareIdentity, oldRecord.userId, oldRecord.callingUid, r.callingUid, r.callerPackage, - SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0); + SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0, 0, + oldRecord.resultToApp != null + ? oldRecord.resultToApp.mState.getCurProcState() + : ActivityManager.PROCESS_STATE_UNKNOWN); } catch (RemoteException e) { Slog.w(TAG, "Failure [" + mQueueName + "] sending broadcast result of " @@ -339,7 +342,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue, BroadcastRecord r, String typeForLogging) { final Intent intent = r.intent; - for (int i = queue.size() - 1; i > 0; i--) { + for (int i = queue.size() - 1; i >= 0; i--) { final BroadcastRecord old = queue.get(i); if (old.userId == r.userId && intent.filterEquals(old.intent)) { if (DEBUG_BROADCAST) { @@ -367,6 +370,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { } r.curApp = app; + r.curAppLastProcessState = app.mState.getCurProcState(); final ProcessReceiverRecord prr = app.mReceivers; prr.addCurReceiver(r); app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); @@ -418,6 +422,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Process cur broadcast " + r + ": NOT STARTED!"); r.curApp = null; + r.curAppLastProcessState = ActivityManager.PROCESS_STATE_UNKNOWN; prr.removeCurReceiver(r); } } @@ -615,7 +620,13 @@ public class BroadcastQueueImpl extends BroadcastQueue { finishTime - r.receiverTime, packageState, r.curApp.info.packageName, - r.callerPackage); + r.callerPackage, + r.calculateTypeForLogging(), + r.getDeliveryGroupPolicy(), + r.intent.getFlags(), + BroadcastRecord.getReceiverPriority(curReceiver), + r.callerProcState, + r.curAppLastProcessState); } if (state == BroadcastRecord.IDLE) { Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE"); @@ -677,6 +688,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.curFilter = null; r.curReceiver = null; r.curApp = null; + r.curAppLastProcessState = ActivityManager.PROCESS_STATE_UNKNOWN; r.curFilteredExtras = null; r.mWasReceiverAppStopped = false; mPendingBroadcast = null; @@ -742,11 +754,12 @@ public class BroadcastQueueImpl extends BroadcastQueue { } } - public void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, + public void performReceiveLocked(BroadcastRecord r, ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, boolean shareIdentity, int sendingUser, int receiverUid, int callingUid, String callingPackage, - long dispatchDelay, long receiveDelay) throws RemoteException { + long dispatchDelay, long receiveDelay, int priority, + int receiverProcessState) throws RemoteException { // If the broadcaster opted-in to sharing their identity, then expose package visibility for // the receiver. if (shareIdentity) { @@ -795,7 +808,9 @@ public class BroadcastQueueImpl extends BroadcastQueue { BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, dispatchDelay, receiveDelay, 0 /* finish_delay */, SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - app != null ? app.info.packageName : null, callingPackage); + app != null ? app.info.packageName : null, callingPackage, + r.calculateTypeForLogging(), r.getDeliveryGroupPolicy(), r.intent.getFlags(), + priority, r.callerProcState, receiverProcessState); } } @@ -842,6 +857,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { // things that directly call the IActivityManager API, which // are already core system stuff so don't matter for this. r.curApp = filter.receiverList.app; + r.curAppLastProcessState = r.curApp.mState.getCurProcState(); filter.receiverList.app.mReceivers.addCurReceiver(r); mService.enqueueOomAdjTargetLocked(r.curApp); mService.updateOomAdjPendingTargetsLocked( @@ -871,12 +887,15 @@ public class BroadcastQueueImpl extends BroadcastQueue { maybeAddBackgroundStartPrivileges(filter.receiverList.app, r); maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options); maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid); - performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, + performReceiveLocked(r, filter.receiverList.app, filter.receiverList.receiver, prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.shareIdentity, r.userId, filter.receiverList.uid, r.callingUid, r.callerPackage, r.dispatchTime - r.enqueueTime, - r.receiverTime - r.dispatchTime); + r.receiverTime - r.dispatchTime, filter.getPriority(), + filter.receiverList.app != null + ? filter.receiverList.app.mState.getCurProcState() + : ActivityManager.PROCESS_STATE_UNKNOWN); // parallel broadcasts are fire-and-forget, not bookended by a call to // finishReceiverLocked(), so we manage their activity-start token here if (filter.receiverList.app != null @@ -1162,12 +1181,15 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.dispatchTime = now; } r.mIsReceiverAppRunning = true; - performReceiveLocked(r.resultToApp, r.resultTo, + performReceiveLocked(r, r.resultToApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false, r.shareIdentity, r.userId, r.callingUid, r.callingUid, r.callerPackage, r.dispatchTime - r.enqueueTime, - now - r.dispatchTime); + now - r.dispatchTime, 0, + r.resultToApp != null + ? r.resultToApp.mState.getCurProcState() + : ActivityManager.PROCESS_STATE_UNKNOWN); logBootCompletedBroadcastCompletionLatencyIfPossible(r); // Set this to null so that the reference // (local and remote) isn't kept in the mBroadcastHistory. @@ -1473,6 +1495,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { r.intent.getAction(), r.getHostingRecordTriggerType()), isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY, (r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false); + r.curAppLastProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; if (r.curApp == null) { // Ah, this recipient is unavailable. Finish it if necessary, // and mark the broadcast record as ready for the next. diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index bb5fcbec7010..76af50de0bd7 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -33,6 +33,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO; import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList; import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList; +import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED; import static com.android.server.am.BroadcastRecord.deliveryStateToString; import static com.android.server.am.BroadcastRecord.getReceiverClassName; import static com.android.server.am.BroadcastRecord.getReceiverPackageName; @@ -68,6 +69,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.text.format.DateUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.MathUtils; @@ -213,6 +215,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue { new AtomicReference<>(); /** + * Container for holding the set of broadcast records that satisfied a certain criteria. + */ + @GuardedBy("mService") + private final AtomicReference<ArrayMap<BroadcastRecord, Boolean>> mRecordsLookupCache = + new AtomicReference<>(); + + /** * Map from UID to its last known "foreground" state. A UID is considered to be in * "foreground" state when it's procState is {@link ActivityManager#PROCESS_STATE_TOP}. * <p> @@ -719,11 +728,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue { private void skipAndCancelReplacedBroadcasts(ArraySet<BroadcastRecord> replacedBroadcasts) { for (int i = 0; i < replacedBroadcasts.size(); ++i) { final BroadcastRecord r = replacedBroadcasts.valueAt(i); - r.resultCode = Activity.RESULT_CANCELED; - r.resultData = null; - r.resultExtras = null; - scheduleResultTo(r); - notifyFinishBroadcast(r); + // Skip all the receivers in the replaced broadcast + for (int rcvrIdx = 0; rcvrIdx < r.receivers.size(); ++rcvrIdx) { + if (!isDeliveryStateTerminal(r.getDeliveryState(rcvrIdx))) { + mBroadcastConsumerSkipAndCanceled.accept(r, rcvrIdx); + } + } } } @@ -731,8 +741,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { if (mService.shouldIgnoreDeliveryGroupPolicy(r.intent.getAction())) { return; } - final int policy = (r.options != null) - ? r.options.getDeliveryGroupPolicy() : BroadcastOptions.DELIVERY_GROUP_POLICY_ALL; + final int policy = r.getDeliveryGroupPolicy(); final BroadcastConsumer broadcastConsumer; switch (policy) { case BroadcastOptions.DELIVERY_GROUP_POLICY_ALL: @@ -742,13 +751,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { broadcastConsumer = mBroadcastConsumerSkipAndCanceled; break; case BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED: + // TODO: Allow applying MERGED policy for broadcasts with more than one receiver. + if (r.receivers.size() > 1) { + return; + } final BundleMerger extrasMerger = r.options.getDeliveryGroupExtrasMerger(); if (extrasMerger == null) { // Extras merger is required to be able to merge the extras. So, if it's not // supplied, then ignore the delivery group policy. return; } - // TODO: Don't merge with the same BroadcastRecord more than once. broadcastConsumer = (record, recordIndex) -> { r.intent.mergeExtras(record.intent, extrasMerger); mBroadcastConsumerSkipAndCanceled.accept(record, recordIndex); @@ -758,6 +770,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { logw("Unknown delivery group policy: " + policy); return; } + final ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = getRecordsLookupCache(); forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { // If the receiver is already in a terminal state, then ignore it. if (isDeliveryStateTerminal(testRecord.getDeliveryState(testIndex))) { @@ -769,22 +782,44 @@ class BroadcastQueueModernImpl extends BroadcastQueue { || !r.matchesDeliveryGroup(testRecord)) { return false; } - // TODO: If a process is in a deferred state, we can always apply the policy as long - // as it is one of the receivers for the new broadcast. // For ordered broadcast, check if the receivers for the new broadcast is a superset // of those for the previous one as skipping and removing only one of them could result // in an inconsistent state. - if (testRecord.ordered || testRecord.resultTo != null) { - // TODO: Cache this result in some way so that we don't have to perform the - // same check for all the broadcast receivers. - return r.containsAllReceivers(testRecord.receivers); - } else if (testRecord.prioritized) { - return r.containsAllReceivers(testRecord.receivers); + if (testRecord.ordered || testRecord.prioritized) { + return containsAllReceivers(r, testRecord, recordsLookupCache); + } else if (testRecord.resultTo != null) { + return testRecord.getDeliveryState(testIndex) == DELIVERY_DEFERRED + ? r.containsReceiver(testRecord.receivers.get(testIndex)) + : containsAllReceivers(r, testRecord, recordsLookupCache); } else { return r.containsReceiver(testRecord.receivers.get(testIndex)); } }, broadcastConsumer, true); + recordsLookupCache.clear(); + mRecordsLookupCache.compareAndSet(null, recordsLookupCache); + } + + @NonNull + private ArrayMap<BroadcastRecord, Boolean> getRecordsLookupCache() { + ArrayMap<BroadcastRecord, Boolean> recordsLookupCache = + mRecordsLookupCache.getAndSet(null); + if (recordsLookupCache == null) { + recordsLookupCache = new ArrayMap<>(); + } + return recordsLookupCache; + } + + private boolean containsAllReceivers(@NonNull BroadcastRecord record, + @NonNull BroadcastRecord testRecord, + @NonNull ArrayMap<BroadcastRecord, Boolean> recordsLookupCache) { + final int idx = recordsLookupCache.indexOfKey(testRecord); + if (idx > 0) { + return recordsLookupCache.valueAt(idx); + } + final boolean containsAll = record.containsAllReceivers(testRecord.receivers); + recordsLookupCache.put(testRecord, containsAll); + return containsAll; } /** @@ -1002,6 +1037,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue { mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent, UserHandle.getAppId(app.uid), r.callingUid, true); } + queue.lastProcessState = app.mState.getCurProcState(); if (receiver instanceof BroadcastFilter) { notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver); thread.scheduleRegisteredReceiver( @@ -1914,12 +1950,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue { ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST; final int type; + final int receiverProcessState; if (queue == null) { type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN; + receiverProcessState = ActivityManager.PROCESS_STATE_UNKNOWN; } else if (queue.getActiveViaColdStart()) { type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; + receiverProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT; } else { type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM; + receiverProcessState = queue.lastProcessState; } // With the new per-process queues, there's no delay between being // "dispatched" and "scheduled", so we report no "receive delay" @@ -1932,7 +1972,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { : SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL; FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName, receiverType, type, dispatchDelay, receiveDelay, finishDelay, packageState, - app != null ? app.info.packageName : null, r.callerPackage); + app != null ? app.info.packageName : null, r.callerPackage, + r.calculateTypeForLogging(), r.getDeliveryGroupPolicy(), r.intent.getFlags(), + BroadcastRecord.getReceiverPriority(receiver), r.callerProcState, + receiverProcessState); } } diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 863dd63a2922..198adcb7606a 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -17,6 +17,19 @@ package com.android.server.am; import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED; +import static android.app.AppProtoEnums.BROADCAST_TYPE_ALARM; +import static android.app.AppProtoEnums.BROADCAST_TYPE_BACKGROUND; +import static android.app.AppProtoEnums.BROADCAST_TYPE_DEFERRABLE_UNTIL_ACTIVE; +import static android.app.AppProtoEnums.BROADCAST_TYPE_FOREGROUND; +import static android.app.AppProtoEnums.BROADCAST_TYPE_INITIAL_STICKY; +import static android.app.AppProtoEnums.BROADCAST_TYPE_INTERACTIVE; +import static android.app.AppProtoEnums.BROADCAST_TYPE_NONE; +import static android.app.AppProtoEnums.BROADCAST_TYPE_ORDERED; +import static android.app.AppProtoEnums.BROADCAST_TYPE_PRIORITIZED; +import static android.app.AppProtoEnums.BROADCAST_TYPE_PUSH_MESSAGE; +import static android.app.AppProtoEnums.BROADCAST_TYPE_PUSH_MESSAGE_OVER_QUOTA; +import static android.app.AppProtoEnums.BROADCAST_TYPE_RESULT_TO; +import static android.app.AppProtoEnums.BROADCAST_TYPE_STICKY; import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_ALL; import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_BACKGROUND_RESTRICTED_ONLY; @@ -31,10 +44,12 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UptimeMillisLong; +import android.app.ActivityManager.ProcessState; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; +import android.app.BroadcastOptions.DeliveryGroupPolicy; import android.app.compat.CompatChanges; import android.content.ComponentName; import android.content.IIntentReceiver; @@ -79,6 +94,7 @@ final class BroadcastRecord extends Binder { final @Nullable String callerFeatureId; // which feature in the package sent this final int callingPid; // the pid of who sent this final int callingUid; // the uid of who sent this + final @ProcessState int callerProcState; // Procstate of the caller process at enqueue time. final int originalStickyCallingUid; // if this is a sticky broadcast, the Uid of the original sender @@ -254,6 +270,8 @@ final class BroadcastRecord extends Binder { BroadcastFilter curFilter; // the registered receiver currently running. Bundle curFilteredExtras; // the bundle that has been filtered by the package visibility rules + int curAppLastProcessState; // The last process state of the current receiver before receiving + boolean mIsReceiverAppRunning; // Was the receiver's app already running. boolean mWasReceiverAppStopped; // Was the receiver app stopped prior to starting @@ -415,13 +433,14 @@ final class BroadcastRecord extends Binder { boolean initialSticky, int userId, @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, - @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) { + @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, + int callerAppProcessState) { this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, serialized, sticky, initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt, - filterExtrasForReceiver); + filterExtrasForReceiver, callerAppProcessState); } BroadcastRecord(BroadcastQueue _queue, @@ -436,7 +455,8 @@ final class BroadcastRecord extends Binder { boolean _initialSticky, int _userId, int originalStickyCallingUid, @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, - @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) { + @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, + int callerAppProcessState) { if (_intent == null) { throw new NullPointerException("Can't construct with a null intent"); } @@ -448,6 +468,7 @@ final class BroadcastRecord extends Binder { callerFeatureId = _callerFeatureId; callingPid = _callingPid; callingUid = _callingUid; + callerProcState = callerAppProcessState; callerInstantApp = _callerInstantApp; callerInstrumented = isCallerInstrumented(_callerApp, _callingUid); resolvedType = _resolvedType; @@ -501,6 +522,7 @@ final class BroadcastRecord extends Binder { callerFeatureId = from.callerFeatureId; callingPid = from.callingPid; callingUid = from.callingUid; + callerProcState = from.callerProcState; callerInstantApp = from.callerInstantApp; callerInstrumented = from.callerInstrumented; ordered = from.ordered; @@ -586,7 +608,8 @@ final class BroadcastRecord extends Binder { requiredPermissions, excludedPermissions, excludedPackages, appOp, options, splitReceivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, - mBackgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver); + mBackgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, + callerProcState); split.enqueueTime = this.enqueueTime; split.enqueueRealTime = this.enqueueRealTime; split.enqueueClockTime = this.enqueueClockTime; @@ -666,7 +689,7 @@ final class BroadcastRecord extends Binder { uid2receiverList.valueAt(i), null /* _resultToApp */, null /* _resultTo */, resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId, mBackgroundStartPrivileges, timeoutExempt, - filterExtrasForReceiver); + filterExtrasForReceiver, callerProcState); br.enqueueTime = this.enqueueTime; br.enqueueRealTime = this.enqueueRealTime; br.enqueueClockTime = this.enqueueClockTime; @@ -1018,6 +1041,46 @@ final class BroadcastRecord extends Binder { } } + int calculateTypeForLogging() { + int type = BROADCAST_TYPE_NONE; + if (isForeground()) { + type |= BROADCAST_TYPE_FOREGROUND; + } else { + type |= BROADCAST_TYPE_BACKGROUND; + } + if (alarm) { + type |= BROADCAST_TYPE_ALARM; + } + if (interactive) { + type |= BROADCAST_TYPE_INTERACTIVE; + } + if (ordered) { + type |= BROADCAST_TYPE_ORDERED; + } + if (prioritized) { + type |= BROADCAST_TYPE_PRIORITIZED; + } + if (resultTo != null) { + type |= BROADCAST_TYPE_RESULT_TO; + } + if (deferUntilActive) { + type |= BROADCAST_TYPE_DEFERRABLE_UNTIL_ACTIVE; + } + if (pushMessage) { + type |= BROADCAST_TYPE_PUSH_MESSAGE; + } + if (pushMessageOverQuota) { + type |= BROADCAST_TYPE_PUSH_MESSAGE_OVER_QUOTA; + } + if (sticky) { + type |= BROADCAST_TYPE_STICKY; + } + if (initialSticky) { + type |= BROADCAST_TYPE_INITIAL_STICKY; + } + return type; + } + public BroadcastRecord maybeStripForHistory() { if (!intent.canStripForHistory()) { return this; @@ -1113,6 +1176,12 @@ final class BroadcastRecord extends Binder { return true; } + @DeliveryGroupPolicy + int getDeliveryGroupPolicy() { + return (options != null) ? options.getDeliveryGroupPolicy() + : BroadcastOptions.DELIVERY_GROUP_POLICY_ALL; + } + boolean matchesDeliveryGroup(@NonNull BroadcastRecord other) { return matchesDeliveryGroup(this, other); } diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index e744eee8b485..3f7d8ba1a120 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -246,9 +246,13 @@ public class ContentProviderHelper { } } + final int callingProcessState = r != null + ? r.mState.getCurProcState() : ActivityManager.PROCESS_STATE_UNKNOWN; + if (providerRunning) { cpi = cpr.info; + if (r != null && cpr.canRunHere(r)) { checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, cpr.name.flattenToShortString(), startTime); @@ -266,7 +270,8 @@ public class ContentProviderHelper { r.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - cpi.packageName, callingPackage); + cpi.packageName, callingPackage, + callingProcessState, callingProcessState); return holder; } @@ -282,6 +287,8 @@ public class ContentProviderHelper { checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, cpr.name.flattenToShortString(), startTime); + final int providerProcessState = cpr.proc.mState.getCurProcState(); + final long origId = Binder.clearCallingIdentity(); try { checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); @@ -338,7 +345,8 @@ public class ContentProviderHelper { cpr.proc.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - cpi.packageName, callingPackage); + cpi.packageName, callingPackage, + callingProcessState, providerProcessState); } } finally { Binder.restoreCallingIdentity(origId); @@ -516,7 +524,8 @@ public class ContentProviderHelper { proc.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM, PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL, - cpi.packageName, callingPackage); + cpi.packageName, callingPackage, + callingProcessState, proc.mState.getCurProcState()); } else { final int packageState = ((cpr.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) @@ -541,7 +550,8 @@ public class ContentProviderHelper { PROVIDER_ACQUISITION_EVENT_REPORTED, proc.uid, callingUid, PROVIDER_ACQUISITION_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD, - packageState, cpi.packageName, callingPackage); + packageState, cpi.packageName, callingPackage, + callingProcessState, ActivityManager.PROCESS_STATE_NONEXISTENT); } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java index 80406e6f66e4..38e7371e7075 100644 --- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java +++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java @@ -113,6 +113,11 @@ public class ForegroundServiceTypeLoggerModule { // We use this to get the duration an API was active after // the stop call. final SparseArray<Long> mLastFgsTimeStamp = new SparseArray<>(); + + // A map of API types to first FGS start call timestamps + // We use this to get the duration an API was active after + // the stop call. + final SparseArray<Long> mFirstFgsTimeStamp = new SparseArray<>(); } // SparseArray that tracks all UIDs that have made various @@ -146,6 +151,7 @@ public class ForegroundServiceTypeLoggerModule { if (fgsIndex < 0) { uidState.mRunningFgs.put(apiType, new ArrayMap<>()); fgsIndex = uidState.mRunningFgs.indexOfKey(apiType); + uidState.mFirstFgsTimeStamp.put(apiType, System.currentTimeMillis()); } final ArrayMap<ComponentName, ServiceRecord> fgsList = uidState.mRunningFgs.valueAt(fgsIndex); @@ -237,7 +243,7 @@ public class ForegroundServiceTypeLoggerModule { // there's no more FGS running for this type, just get rid of it uidState.mRunningFgs.remove(apiType); // but we need to keep track of the timestamp in case an API stops - uidState.mLastFgsTimeStamp.put(apiType, record.mFgsExitTime); + uidState.mLastFgsTimeStamp.put(apiType, System.currentTimeMillis()); } } if (!apisFound.isEmpty()) { @@ -454,8 +460,17 @@ public class ForegroundServiceTypeLoggerModule { public void logFgsApiEvent(ServiceRecord r, int fgsState, @FgsApiState int apiState, @ForegroundServiceApiType int apiType, long timestamp) { - final long apiDurationBeforeFgsStart = r.mFgsEnterTime - timestamp; - final long apiDurationAfterFgsEnd = timestamp - r.mFgsExitTime; + long apiDurationBeforeFgsStart = r.createRealTime - timestamp; + long apiDurationAfterFgsEnd = timestamp - r.mFgsExitTime; + UidState uidState = mUids.get(r.appInfo.uid); + if (uidState != null) { + if (uidState.mFirstFgsTimeStamp.contains(apiType)) { + apiDurationBeforeFgsStart = uidState.mFirstFgsTimeStamp.get(apiType) - timestamp; + } + if (uidState.mLastFgsTimeStamp.contains(apiType)) { + apiDurationAfterFgsEnd = timestamp - uidState.mLastFgsTimeStamp.get(apiType); + } + } final int[] apiTypes = new int[1]; apiTypes[0] = apiType; final long[] timeStamps = new long[1]; diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 6c3f01e6cd3a..764bbe8bd191 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1471,7 +1471,8 @@ public class OomAdjuster { if (!ActivityManager.isProcStateBackground(uidRec.getSetProcState()) || uidRec.isSetAllowListed()) { uidRec.setLastBackgroundTime(nowElapsed); - if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { // Note: the background settle time is in elapsed realtime, while // the handler time base is uptime. All this means is that we may // stop background uids later than we had intended, but that only @@ -3188,7 +3189,6 @@ public class OomAdjuster { } if (state.getCurCapability() != state.getSetCapability()) { - changes |= ActivityManagerService.ProcessChangeItem.CHANGE_CAPABILITY; state.setSetCapability(state.getCurCapability()); } @@ -3212,13 +3212,12 @@ public class OomAdjuster { mProcessList.enqueueProcessChangeItemLocked(app.getPid(), app.info.uid); item.changes |= changes; item.foregroundActivities = state.hasRepForegroundActivities(); - item.capability = state.getSetCapability(); if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS, "Item " + Integer.toHexString(System.identityHashCode(item)) + " " + app.toShortString() + ": changes=" + item.changes + " foreground=" + item.foregroundActivities + " type=" + state.getAdjType() + " source=" + state.getAdjSource() - + " target=" + state.getAdjTarget() + " capability=" + item.capability); + + " target=" + state.getAdjTarget()); } if (state.isCached() && !state.shouldNotKillOnBgRestrictedAndIdle()) { @@ -3229,7 +3228,8 @@ public class OomAdjuster { // (for states debouncing to avoid from thrashing). state.setLastCanKillOnBgRestrictedAndIdleTime(nowElapsed); // Kick off the delayed checkup message if needed. - if (!mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs); } @@ -3348,6 +3348,7 @@ public class OomAdjuster { @GuardedBy("mService") void idleUidsLocked() { final int N = mActiveUids.size(); + mService.mHandler.removeMessages(IDLE_UIDS_MSG); if (N <= 0) { return; } @@ -3393,7 +3394,6 @@ public class OomAdjuster { } } if (nextTime > 0) { - mService.mHandler.removeMessages(IDLE_UIDS_MSG); mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed); } diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java index 7841b699ec98..ffe5a6e6b958 100644 --- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java +++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java @@ -18,6 +18,7 @@ package com.android.server.am; import android.annotation.UptimeMillisLong; import android.app.ActivityManagerInternal.OomAdjReason; +import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -320,5 +321,8 @@ final class ProcessCachedOptimizerRecord { pw.print(prefix); pw.print("isFreezeExempt="); pw.print(mFreezeExempt); pw.print(" isPendingFreeze="); pw.print(mPendingFreeze); pw.print(" " + IS_FROZEN + "="); pw.println(mFrozen); + pw.print(prefix); pw.print("earliestFreezableTimeMs="); + TimeUtils.formatDuration(mEarliestFreezableTimeMillis, nowUptime, pw); + pw.println(); } } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 4342cb994754..c5776d822c8f 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2572,9 +2572,14 @@ public final class ProcessList { // and did the cleanup before the actual death notification. Check the dying processes. predecessor = mDyingProcesses.get(processName, info.uid); if (predecessor != null) { - if (app != null) { + // The process record could have existed but its pid is set to 0. In this case, + // the 'app' and 'predecessor' could end up pointing to the same instance; + // so make sure we check this case here. + if (app != null && app != predecessor) { app.mPredecessor = predecessor; predecessor.mSuccessor = app; + } else { + app = null; } Slog.w(TAG_PROCESSES, predecessor.toString() + " is attached to a previous process " + predecessor.getDyingPid()); @@ -5195,6 +5200,8 @@ public final class ProcessList { mDyingProcesses.remove(app.processName, app.uid); app.setDyingPid(0); handlePrecedingAppDiedLocked(app); + // Remove from the LRU list if it's still there. + removeLruProcessLocked(app); return true; } return false; @@ -5243,7 +5250,9 @@ public final class ProcessList { mAppsInBackgroundRestricted.add(app); final long future = killAppIfBgRestrictedAndCachedIdleLocked( app, nowElapsed); - if (future > 0 && !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) { + if (future > 0 + && (mService.mDeterministicUidIdle + || !mService.mHandler.hasMessages(IDLE_UIDS_MSG))) { mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, future - nowElapsed); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index d6495c78574c..267d2464e0d5 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -669,6 +669,11 @@ class ProcessRecord implements WindowProcessListener { return mOnewayThread; } + @GuardedBy(anyOf = {"mService", "mProcLock"}) + int getCurProcState() { + return mState.getCurProcState(); + } + @GuardedBy({"mService", "mProcLock"}) public void makeActive(IApplicationThread thread, ProcessStatsService tracker) { mProfile.onProcessActive(thread, tracker); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 1f39d1b5d7f8..50fe6d71d26e 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE; @@ -177,12 +178,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN @PowerExemptionManager.ReasonCode int mAllowWhileInUsePermissionInFgsReason = REASON_DENIED; - // Integer version of mAllowWhileInUsePermissionInFgs that we keep track to compare - // the old and new logics. - // TODO: Remove them once we're confident in the new logic. - int mDebugWhileInUseReasonInStartForeground = REASON_DENIED; - int mDebugWhileInUseReasonInBindService = REASON_DENIED; - // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state. boolean mAllowWhileInUsePermissionInFgsAtEntering; /** Allow scheduling user-initiated jobs from the background. */ @@ -224,13 +219,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // is called. int mStartForegroundCount; - // Last time mAllowWhileInUsePermissionInFgs or mAllowStartForeground was set to "allowed" - // from "disallowed" when the service was _not_ already a foreground service. - // this means they're set in startService(). (not startForegroundService) - // In startForeground(), if this timestamp is too old, we can't trust these flags, so - // we need to reset them. - long mLastUntrustedSetFgsRestrictionAllowedTime; - // This is a service record of a FGS delegate (not a service record of a real service) boolean mIsFgsDelegate; @Nullable ForegroundServiceDelegation mFgsDelegation; @@ -257,6 +245,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN */ long mRestartSchedulingTime; + /** + * The snapshot process state when the service is requested (either start or bind). + */ + int mProcessStateOnRequest; + static class StartItem { final ServiceRecord sr; final boolean taskRemoved; @@ -266,6 +259,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN final Intent intent; final NeededUriGrants neededGrants; final @Nullable String mCallingPackageName; + final int mCallingProcessState; long deliveredTime; int deliveryCount; int doneExecutingCount; @@ -275,7 +269,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, NeededUriGrants _neededGrants, int _callingId, - String callingProcessName, @Nullable String callingPackageName) { + String callingProcessName, @Nullable String callingPackageName, + int callingProcessState) { sr = _sr; taskRemoved = _taskRemoved; id = _id; @@ -284,6 +279,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN callingId = _callingId; mCallingProcessName = callingProcessName; mCallingPackageName = callingPackageName; + mCallingProcessState = callingProcessState; } UriPermissionOwner getUriPermissionsLocked() { @@ -624,12 +620,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason="); pw.println(PowerExemptionManager.reasonCodeToString(mAllowWhileInUsePermissionInFgsReason)); - pw.print(prefix); pw.print("debugWhileInUseReasonInStartForeground="); - pw.println(PowerExemptionManager.reasonCodeToString( - mDebugWhileInUseReasonInStartForeground)); - pw.print(prefix); pw.print("debugWhileInUseReasonInBindService="); - pw.println(PowerExemptionManager.reasonCodeToString(mDebugWhileInUseReasonInBindService)); - pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage); @@ -642,10 +632,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("infoAllowStartForeground="); pw.println(mInfoAllowStartForeground); - pw.print(prefix); pw.print("lastUntrustedSetFgsRestrictionAllowedTime="); - TimeUtils.formatDuration(mLastUntrustedSetFgsRestrictionAllowedTime, now, pw); - pw.println(); - if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); } @@ -896,6 +882,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN app.mServices.updateHostingComonentTypeForBindingsLocked(); } app = proc; + updateProcessStateOnRequest(); if (pendingConnectionGroup > 0 && proc != null) { final ProcessServiceRecord psr = proc.mServices; psr.setConnectionService(this); @@ -922,6 +909,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } + void updateProcessStateOnRequest() { + mProcessStateOnRequest = app != null && app.getThread() != null && !app.isKilled() + ? app.mState.getCurProcState() : PROCESS_STATE_NONEXISTENT; + } + @NonNull ArrayMap<IBinder, ArrayList<ConnectionRecord>> getConnections() { return connections; @@ -1084,12 +1076,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN if (app == null) { return; } - if (mBackgroundStartPrivilegesByStartMerged.allowsAny() - || mIsAllowedBgActivityStartsByBinding) { + BackgroundStartPrivileges backgroundStartPrivileges = + getBackgroundStartPrivilegesWithExclusiveToken(); + if (backgroundStartPrivileges.allowsAny()) { // if the token is already there it's safe to "re-add it" - we're dealing with // a set of Binder objects app.addOrUpdateBackgroundStartPrivileges(this, - getBackgroundStartPrivilegesWithExclusiveToken()); + backgroundStartPrivileges); } else { app.removeBackgroundStartPrivileges(this); } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 8c227f5488d3..9db9e77c82c9 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -72,6 +72,10 @@ public class SettingsToPropertiesMapper { Settings.Global.NATIVE_FLAGS_HEALTH_CHECK_ENABLED, }; + // TODO(b/282593625): Move this constant to DeviceConfig module + private static final String NAMESPACE_TETHERING_U_OR_LATER_NATIVE = + "tethering_u_or_later_native"; + // All the flags under the listed DeviceConfig scopes will be synced to native level. // // NOTE: please grant write permission system property prefix @@ -106,7 +110,8 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, DeviceConfig.NAMESPACE_MEMORY_SAFETY_NATIVE_BOOT, DeviceConfig.NAMESPACE_MEMORY_SAFETY_NATIVE, - DeviceConfig.NAMESPACE_HDMI_CONTROL + DeviceConfig.NAMESPACE_HDMI_CONTROL, + NAMESPACE_TETHERING_U_OR_LATER_NATIVE }; private final String[] mGlobalSettings; diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java index 01fb0d1d8b18..af99684db6f4 100644 --- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java +++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java @@ -392,11 +392,9 @@ public class StackTracesDumpHelper { if (TEMP_DUMP_TIME_LIMIT <= timeTaken) { Slog.e(TAG, "Aborted stack trace dump (current primary pid=" + pid + "); deadline exceeded."); - tmpTracesFile.delete(); if (latencyTracker != null) { latencyTracker.dumpStackTracesTempFileTimedOut(); } - return null; } if (DEBUG_ANR) { Slog.d(TAG, "Done with primary pid " + pid + " in " + timeTaken + "ms" diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java index a2582083c409..7eeec32cf24a 100644 --- a/services/core/java/com/android/server/am/UidObserverController.java +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -104,40 +105,62 @@ public class UidObserverController { } } - void addUidToObserver(@NonNull IBinder observerToken, int uid) { - synchronized (mLock) { - int i = mUidObservers.beginBroadcast(); - while (i-- > 0) { - var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); - if (reg.getToken().equals(observerToken)) { - reg.addUid(uid); - break; - } + final void addUidToObserver(@NonNull IBinder observerToken, int uid) { + Message msg = Message.obtain(mHandler, ActivityManagerService.ADD_UID_TO_OBSERVER_MSG, + uid, /*arg2*/ 0, observerToken); + mHandler.sendMessage(msg); + } - if (i == 0) { - Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); - } + /** + * Add a uid to the list of uids an observer is interested in. Must be run on the same thread + * as mDispatchRunnable. + * + * @param observerToken The token identifier for a UidObserver + * @param uid The uid to add to the list of watched uids + */ + public final void addUidToObserverImpl(@NonNull IBinder observerToken, int uid) { + int i = mUidObservers.beginBroadcast(); + while (i-- > 0) { + var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); + if (reg.getToken().equals(observerToken)) { + reg.addUid(uid); + break; + } + + if (i == 0) { + Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } - mUidObservers.finishBroadcast(); } + mUidObservers.finishBroadcast(); } - void removeUidFromObserver(@NonNull IBinder observerToken, int uid) { - synchronized (mLock) { - int i = mUidObservers.beginBroadcast(); - while (i-- > 0) { - var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); - if (reg.getToken().equals(observerToken)) { - reg.removeUid(uid); - break; - } + final void removeUidFromObserver(@NonNull IBinder observerToken, int uid) { + Message msg = Message.obtain(mHandler, ActivityManagerService.REMOVE_UID_FROM_OBSERVER_MSG, + uid, /*arg2*/ 0, observerToken); + mHandler.sendMessage(msg); + } - if (i == 0) { - Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); - } + /** + * Remove a uid from the list of uids an observer is interested in. Must be run on the same + * thread as mDispatchRunnable. + * + * @param observerToken The token identifier for a UidObserver + * @param uid The uid to remove from the list of watched uids + */ + public final void removeUidFromObserverImpl(@NonNull IBinder observerToken, int uid) { + int i = mUidObservers.beginBroadcast(); + while (i-- > 0) { + var reg = (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); + if (reg.getToken().equals(observerToken)) { + reg.removeUid(uid); + break; + } + + if (i == 0) { + Slog.e(TAG_UID_OBSERVERS, "Unable to find UidObserver by token"); } - mUidObservers.finishBroadcast(); } + mUidObservers.finishBroadcast(); } int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, @@ -429,21 +452,23 @@ public class UidObserverController { } } - pw.println(); - pw.print(" mUidChangeDispatchCount="); - pw.print(mUidChangeDispatchCount); - pw.println(); - pw.println(" Slow UID dispatches:"); - for (int i = 0; i < count; i++) { - final UidObserverRegistration reg = (UidObserverRegistration) - mUidObservers.getRegisteredCallbackCookie(i); - pw.print(" "); - pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName()); - pw.print(": "); - pw.print(reg.mSlowDispatchCount); - pw.print(" / Max "); - pw.print(reg.mMaxDispatchTime); - pw.println("ms"); + if (dumpPackage == null) { + pw.println(); + pw.print(" mUidChangeDispatchCount="); + pw.print(mUidChangeDispatchCount); + pw.println(); + pw.println(" Slow UID dispatches:"); + for (int i = 0; i < count; i++) { + final UidObserverRegistration reg = (UidObserverRegistration) + mUidObservers.getRegisteredCallbackCookie(i); + pw.print(" "); + pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName()); + pw.print(": "); + pw.print(reg.mSlowDispatchCount); + pw.print(" / Max "); + pw.print(reg.mMaxDispatchTime); + pw.println("ms"); + } } } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index d7a5ee963449..29a19417b8fd 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -1018,13 +1018,15 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void clearA2dpSuspended() { + /*package*/ void clearA2dpSuspended(boolean internalOnly) { if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "clearA2dpSuspended"); + Log.v(TAG, "clearA2dpSuspended, internalOnly: " + internalOnly); } synchronized (mBluetoothAudioStateLock) { mBluetoothA2dpSuspendedInt = false; - mBluetoothA2dpSuspendedExt = false; + if (!internalOnly) { + mBluetoothA2dpSuspendedExt = false; + } updateAudioHalBluetoothState(); } } @@ -1046,13 +1048,15 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void clearLeAudioSuspended() { + /*package*/ void clearLeAudioSuspended(boolean internalOnly) { if (AudioService.DEBUG_COMM_RTE) { - Log.v(TAG, "clearLeAudioSuspended"); + Log.v(TAG, "clearLeAudioSuspended, internalOnly: " + internalOnly); } synchronized (mBluetoothAudioStateLock) { mBluetoothLeSuspendedInt = false; - mBluetoothLeSuspendedExt = false; + if (!internalOnly) { + mBluetoothLeSuspendedExt = false; + } updateAudioHalBluetoothState(); } } @@ -2205,19 +2209,20 @@ import java.util.concurrent.atomic.AtomicBoolean; if (preferredCommunicationDevice == null) { AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice(); if (defaultDevice != null) { - mDeviceInventory.setPreferredDevicesForStrategy( + mDeviceInventory.setPreferredDevicesForStrategyInt( mCommunicationStrategyId, Arrays.asList(defaultDevice)); - mDeviceInventory.setPreferredDevicesForStrategy( + mDeviceInventory.setPreferredDevicesForStrategyInt( mAccessibilityStrategyId, Arrays.asList(defaultDevice)); } else { - mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId); - mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId); + mDeviceInventory.removePreferredDevicesForStrategyInt(mCommunicationStrategyId); + mDeviceInventory.removePreferredDevicesForStrategyInt(mAccessibilityStrategyId); } mDeviceInventory.applyConnectedDevicesRoles(); + mDeviceInventory.reapplyExternalDevicesRoles(); } else { - mDeviceInventory.setPreferredDevicesForStrategy( + mDeviceInventory.setPreferredDevicesForStrategyInt( mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice)); - mDeviceInventory.setPreferredDevicesForStrategy( + mDeviceInventory.setPreferredDevicesForStrategyInt( mAccessibilityStrategyId, Arrays.asList(preferredCommunicationDevice)); } onUpdatePhoneStrategyDevice(preferredCommunicationDevice); diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index a561612b99c6..ec85d577a8e7 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -67,6 +67,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; +import java.util.stream.Stream; /** * Class to manage the inventory of all connected devices. @@ -324,14 +325,22 @@ public class AudioDeviceInventory { mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { pw.println(" " + prefix + "capturePreset:" + capturePreset + " devices:" + devices); }); - pw.println("\n" + prefix + "Applied devices roles for strategies:"); + pw.println("\n" + prefix + "Applied devices roles for strategies (from API):"); mAppliedStrategyRoles.forEach((key, devices) -> { pw.println(" " + prefix + "strategy: " + key.first + " role:" + key.second + " devices:" + devices); }); - pw.println("\n" + prefix + "Applied devices roles for presets:"); + pw.println("\n" + prefix + "Applied devices roles for strategies (internal):"); + mAppliedStrategyRolesInt.forEach((key, devices) -> { + pw.println(" " + prefix + "strategy: " + key.first + + " role:" + key.second + " devices:" + devices); }); + pw.println("\n" + prefix + "Applied devices roles for presets (from API):"); mAppliedPresetRoles.forEach((key, devices) -> { pw.println(" " + prefix + "preset: " + key.first + " role:" + key.second + " devices:" + devices); }); + pw.println("\n" + prefix + "Applied devices roles for presets (internal:"); + mAppliedPresetRolesInt.forEach((key, devices) -> { + pw.println(" " + prefix + "preset: " + key.first + + " role:" + key.second + " devices:" + devices); }); } //------------------------------------------------------------ @@ -352,20 +361,34 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, di.mDeviceCodecFormat); } - mAppliedStrategyRoles.clear(); + mAppliedStrategyRolesInt.clear(); + mAppliedPresetRolesInt.clear(); applyConnectedDevicesRoles_l(); } + reapplyExternalDevicesRoles(); + } + + /*package*/ void reapplyExternalDevicesRoles() { + synchronized (mDevicesLock) { + mAppliedStrategyRoles.clear(); + mAppliedPresetRoles.clear(); + } synchronized (mPreferredDevices) { mPreferredDevices.forEach((strategy, devices) -> { - setPreferredDevicesForStrategy(strategy, devices); }); + setPreferredDevicesForStrategy(strategy, devices); + }); } synchronized (mNonDefaultDevices) { mNonDefaultDevices.forEach((strategy, devices) -> { - addDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); }); + addDevicesRoleForStrategy(strategy, AudioSystem.DEVICE_ROLE_DISABLED, + devices, false /* internal */); + }); } synchronized (mPreferredDevicesForCapturePreset) { - // TODO: call audiosystem to restore + mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { + setDevicesRoleForCapturePreset( + capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + }); } } @@ -768,7 +791,7 @@ public class AudioDeviceInventory { } return status; } - + // Only used for external requests coming from an API /*package*/ int setPreferredDevicesForStrategy(int strategy, @NonNull List<AudioDeviceAttributes> devices) { int status = AudioSystem.ERROR; @@ -777,10 +800,17 @@ public class AudioDeviceInventory { "setPreferredDevicesForStrategy, strategy: " + strategy + " devices: " + devices)).printLog(TAG)); status = setDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, false /* internal */); } return status; } + // Only used for internal requests + /*package*/ int setPreferredDevicesForStrategyInt(int strategy, + @NonNull List<AudioDeviceAttributes> devices) { + + return setDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, true /* internal */); + } /*package*/ int removePreferredDevicesForStrategyAndSave(int strategy) { final int status = removePreferredDevicesForStrategy(strategy); @@ -789,7 +819,7 @@ public class AudioDeviceInventory { } return status; } - + // Only used for external requests coming from an API /*package*/ int removePreferredDevicesForStrategy(int strategy) { int status = AudioSystem.ERROR; @@ -799,10 +829,15 @@ public class AudioDeviceInventory { + strategy)).printLog(TAG)); status = clearDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_PREFERRED); + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, false /*internal */); } return status; } + // Only used for internal requests + /*package*/ int removePreferredDevicesForStrategyInt(int strategy) { + return clearDevicesRoleForStrategy( + strategy, AudioSystem.DEVICE_ROLE_PREFERRED, true /*internal */); + } /*package*/ int setDeviceAsNonDefaultForStrategyAndSave(int strategy, @NonNull AudioDeviceAttributes device) { @@ -816,7 +851,7 @@ public class AudioDeviceInventory { "setDeviceAsNonDefaultForStrategyAndSave, strategy: " + strategy + " device: " + device)).printLog(TAG)); status = addDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); + strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */); } if (status == AudioSystem.SUCCESS) { @@ -838,7 +873,7 @@ public class AudioDeviceInventory { + strategy + " devices: " + device)).printLog(TAG)); status = removeDevicesRoleForStrategy( - strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); + strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */); } if (status == AudioSystem.SUCCESS) { @@ -877,6 +912,7 @@ public class AudioDeviceInventory { return status; } + // Only used for external requests coming from an API private int setPreferredDevicesForCapturePreset( int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { int status = AudioSystem.ERROR; @@ -895,6 +931,7 @@ public class AudioDeviceInventory { return status; } + // Only used for external requests coming from an API private int clearPreferredDevicesForCapturePreset(int capturePreset) { int status = AudioSystem.ERROR; @@ -905,20 +942,23 @@ public class AudioDeviceInventory { return status; } - private int addDevicesRoleForCapturePreset(int capturePreset, int role, + // Only used for internal requests + private int addDevicesRoleForCapturePresetInt(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { - return addDevicesRole(mAppliedPresetRoles, (p, r, d) -> { + return addDevicesRole(mAppliedPresetRolesInt, (p, r, d) -> { return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); }, capturePreset, role, devices); } - private int removeDevicesRoleForCapturePreset(int capturePreset, int role, + // Only used for internal requests + private int removeDevicesRoleForCapturePresetInt(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { - return removeDevicesRole(mAppliedPresetRoles, (p, r, d) -> { + return removeDevicesRole(mAppliedPresetRolesInt, (p, r, d) -> { return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); }, capturePreset, role, devices); } + // Only used for external requests coming from an API private int setDevicesRoleForCapturePreset(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> { @@ -928,6 +968,7 @@ public class AudioDeviceInventory { }, capturePreset, role, devices); } + // Only used for external requests coming from an API private int clearDevicesRoleForCapturePreset(int capturePreset, int role) { return clearDevicesRole(mAppliedPresetRoles, (p, r, d) -> { return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); @@ -945,32 +986,39 @@ public class AudioDeviceInventory { } private int addDevicesRoleForStrategy(int strategy, int role, - @NonNull List<AudioDeviceAttributes> devices) { - return addDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.setDevicesRoleForStrategy(s, r, d); - }, strategy, role, devices); + @NonNull List<AudioDeviceAttributes> devices, + boolean internal) { + return addDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, + (s, r, d) -> { + return mAudioSystem.setDevicesRoleForStrategy(s, r, d); + }, strategy, role, devices); } private int removeDevicesRoleForStrategy(int strategy, int role, - @NonNull List<AudioDeviceAttributes> devices) { - return removeDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); - }, strategy, role, devices); + @NonNull List<AudioDeviceAttributes> devices, + boolean internal) { + return removeDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, + (s, r, d) -> { + return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); + }, strategy, role, devices); } private int setDevicesRoleForStrategy(int strategy, int role, - @NonNull List<AudioDeviceAttributes> devices) { - return setDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.setDevicesRoleForStrategy(s, r, d); - }, (s, r, d) -> { - return mAudioSystem.clearDevicesRoleForStrategy(s, r); - }, strategy, role, devices); + @NonNull List<AudioDeviceAttributes> devices, + boolean internal) { + return setDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, + (s, r, d) -> { + return mAudioSystem.setDevicesRoleForStrategy(s, r, d); + }, (s, r, d) -> { + return mAudioSystem.clearDevicesRoleForStrategy(s, r); + }, strategy, role, devices); } - private int clearDevicesRoleForStrategy(int strategy, int role) { - return clearDevicesRole(mAppliedStrategyRoles, (s, r, d) -> { - return mAudioSystem.clearDevicesRoleForStrategy(s, r); - }, strategy, role); + private int clearDevicesRoleForStrategy(int strategy, int role, boolean internal) { + return clearDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, + (s, r, d) -> { + return mAudioSystem.clearDevicesRoleForStrategy(s, r); + }, strategy, role); } //------------------------------------------------------------ @@ -978,16 +1026,25 @@ public class AudioDeviceInventory { // same list of devices for a given role and strategy and the corresponding systematic // redundant work in audio policy manager and audio flinger. // The key is the pair <Strategy , Role> and the value is the current list of devices. - + // mAppliedStrategyRoles is for requests coming from an API. + // mAppliedStrategyRolesInt is for internal requests. Entries are removed when the requested + // device is disconnected. private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> mAppliedStrategyRoles = new ArrayMap<>(); + private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> + mAppliedStrategyRolesInt = new ArrayMap<>(); // Cache for applied roles for capture presets and devices. The cache avoids reapplying the // same list of devices for a given role and capture preset and the corresponding systematic // redundant work in audio policy manager and audio flinger. // The key is the pair <Preset , Role> and the value is the current list of devices. + // mAppliedPresetRoles is for requests coming from an API. + // mAppliedPresetRolesInt is for internal requests. Entries are removed when the requested + // device is disconnected. private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> mAppliedPresetRoles = new ArrayMap<>(); + private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> + mAppliedPresetRolesInt = new ArrayMap<>(); interface AudioSystemInterface { int deviceRoleAction(int usecase, int role, @Nullable List<AudioDeviceAttributes> devices); @@ -1113,10 +1170,11 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void purgeDevicesRoles_l() { - purgeRoles(mAppliedStrategyRoles, (s, r, d) -> { + purgeRoles(mAppliedStrategyRolesInt, (s, r, d) -> { return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); }); - purgeRoles(mAppliedPresetRoles, (p, r, d) -> { + purgeRoles(mAppliedPresetRolesInt, (p, r, d) -> { return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); }); + reapplyExternalDevicesRoles(); } @GuardedBy("mDevicesLock") @@ -1124,8 +1182,12 @@ public class AudioDeviceInventory { ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, AudioSystemInterface asi) { synchronized (rolesMap) { + AudioDeviceInfo[] connectedDevices = AudioManager.getDevicesStatic( + AudioManager.GET_DEVICES_ALL); + Iterator<Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole = rolesMap.entrySet().iterator(); + while (itRole.hasNext()) { Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry = itRole.next(); @@ -1133,9 +1195,15 @@ public class AudioDeviceInventory { Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator(); while (itDev.hasNext()) { AudioDeviceAttributes ada = itDev.next(); - final String devKey = DeviceInfo.makeDeviceListKey(ada.getInternalType(), - ada.getAddress()); - if (mConnectedDevices.get(devKey) == null) { + + AudioDeviceInfo device = Stream.of(connectedDevices) + .filter(d -> d.getInternalType() == ada.getInternalType()) + .filter(d -> (!AudioSystem.isBluetoothDevice(d.getInternalType()) + || (d.getAddress() == ada.getAddress()))) + .findFirst() + .orElse(null); + + if (device == null) { asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada)); itDev.remove(); } @@ -1479,7 +1547,7 @@ public class AudioDeviceInventory { } // Reset A2DP suspend state each time a new sink is connected - mDeviceBroker.clearA2dpSuspended(); + mDeviceBroker.clearA2dpSuspended(true /* internalOnly */); // The convention for head tracking sensors associated with A2DP devices is to // use a UUID derived from the MAC address as follows: @@ -1582,10 +1650,12 @@ public class AudioDeviceInventory { } if (disable) { addDevicesRoleForStrategy(strategy.getId(), - AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); + AudioSystem.DEVICE_ROLE_DISABLED, + Arrays.asList(ada), true /* internal */); } else { removeDevicesRoleForStrategy(strategy.getId(), - AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); + AudioSystem.DEVICE_ROLE_DISABLED, + Arrays.asList(ada), true /* internal */); } } } @@ -1602,10 +1672,10 @@ public class AudioDeviceInventory { + ", disable: " + disable); } if (disable) { - addDevicesRoleForCapturePreset(capturePreset, + addDevicesRoleForCapturePresetInt(capturePreset, AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); } else { - removeDevicesRoleForCapturePreset(capturePreset, + removeDevicesRoleForCapturePresetInt(capturePreset, AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); } } @@ -1900,7 +1970,7 @@ public class AudioDeviceInventory { "LE Audio device addr=" + address + " now available").printLog(TAG)); } // Reset LEA suspend state each time a new sink is connected - mDeviceBroker.clearLeAudioSuspended(); + mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */); UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada); mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d0b6cdce037f..c177c4efb5ef 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -353,7 +353,6 @@ public class AudioService extends IAudioService.Stub private static final int MSG_PLAY_SOUND_EFFECT = 5; private static final int MSG_LOAD_SOUND_EFFECTS = 7; private static final int MSG_SET_FORCE_USE = 8; - private static final int MSG_BT_HEADSET_CNCT_FAILED = 9; private static final int MSG_SET_ALL_VOLUMES = 10; private static final int MSG_UNLOAD_SOUND_EFFECTS = 15; private static final int MSG_SYSTEM_READY = 16; @@ -1368,6 +1367,7 @@ public class AudioService extends IAudioService.Stub intentFilter.addAction(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); intentFilter.addAction(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); intentFilter.addAction(ACTION_CHECK_MUSIC_ACTIVE); + intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null, Context.RECEIVER_EXPORTED); @@ -3277,6 +3277,7 @@ public class AudioService extends IAudioService.Stub if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1 streamType = mVolumeControlStream; } else { + // TODO discard activity on a muted stream? final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType); final boolean activeForReal; if (maybeActiveStreamType == AudioSystem.STREAM_RING @@ -3480,9 +3481,10 @@ public class AudioService extends IAudioService.Stub } } else if (isStreamMutedByRingerOrZenMode(streamTypeAlias) && streamState.mIsMuted) { // if the stream is currently muted streams by ringer/zen mode - // then it cannot be unmuted (without FLAG_ALLOW_RINGER_MODES) + // then it cannot be unmuted (without FLAG_ALLOW_RINGER_MODES) with an unmute or raise if (direction == AudioManager.ADJUST_TOGGLE_MUTE - || direction == AudioManager.ADJUST_UNMUTE) { + || direction == AudioManager.ADJUST_UNMUTE + || direction == AudioManager.ADJUST_RAISE) { adjustVolume = false; } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index e46c3cc4a285..c8a17e5eea5a 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -445,8 +445,8 @@ public class BtHelper { /*package*/ synchronized void resetBluetoothSco() { mScoAudioState = SCO_STATE_INACTIVE; broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - mDeviceBroker.clearA2dpSuspended(); - mDeviceBroker.clearLeAudioSuspended(); + mDeviceBroker.clearA2dpSuspended(false /* internalOnly */); + mDeviceBroker.clearLeAudioSuspended(false /* internalOnly */); mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); } diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 1989bc77583a..8ef2a1bd26c2 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -76,7 +76,7 @@ import java.util.function.Function; */ public final class AuthSession implements IBinder.DeathRecipient { private static final String TAG = "BiometricService/AuthSession"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; /* * Defined in biometrics.proto @@ -118,6 +118,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @VisibleForTesting final IBinder mToken; // Info to be shown on BiometricDialog when all cookies are returned. @VisibleForTesting final PromptInfo mPromptInfo; + @VisibleForTesting final BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger; private final long mRequestId; private final long mOperationId; private final int mUserId; @@ -164,6 +165,32 @@ public final class AuthSession implements IBinder.DeathRecipient { @NonNull PromptInfo promptInfo, boolean debugEnabled, @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) { + this(context, biometricContext, statusBarService, sysuiReceiver, keystore, random, + clientDeathReceiver, preAuthInfo, token, requestId, operationId, userId, + sensorReceiver, clientReceiver, opPackageName, promptInfo, debugEnabled, + fingerprintSensorProperties, BiometricFrameworkStatsLogger.getInstance()); + } + + @VisibleForTesting + AuthSession(@NonNull Context context, + @NonNull BiometricContext biometricContext, + @NonNull IStatusBarService statusBarService, + @NonNull IBiometricSysuiReceiver sysuiReceiver, + @NonNull KeyStore keystore, + @NonNull Random random, + @NonNull ClientDeathReceiver clientDeathReceiver, + @NonNull PreAuthInfo preAuthInfo, + @NonNull IBinder token, + long requestId, + long operationId, + int userId, + @NonNull IBiometricSensorReceiver sensorReceiver, + @NonNull IBiometricServiceReceiver clientReceiver, + @NonNull String opPackageName, + @NonNull PromptInfo promptInfo, + boolean debugEnabled, + @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties, + @NonNull BiometricFrameworkStatsLogger logger) { Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo); mContext = context; mBiometricContext = biometricContext; @@ -184,6 +211,7 @@ public final class AuthSession implements IBinder.DeathRecipient { mDebugEnabled = debugEnabled; mFingerprintSensorProperties = fingerprintSensorProperties; mCancelled = false; + mBiometricFrameworkStatsLogger = logger; try { mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */); @@ -708,7 +736,7 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } - BiometricFrameworkStatsLogger.getInstance().authenticate( + mBiometricFrameworkStatsLogger.authenticate( mBiometricContext.updateContext(new OperationContextExt(true /* isBP */), isCrypto()), statsModality(), @@ -723,11 +751,17 @@ public final class AuthSession implements IBinder.DeathRecipient { } else { final long latency = System.currentTimeMillis() - mStartTimeMs; - int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE - ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON - : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL - ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED - : 0; + int error = 0; + switch(reason) { + case BiometricPrompt.DISMISSED_REASON_NEGATIVE: + error = BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON; + break; + case BiometricPrompt.DISMISSED_REASON_USER_CANCEL: + error = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED; + break; + default: + } + if (DEBUG) { Slog.v(TAG, "Dismissed! Modality: " + statsModality() + ", User: " + mUserId @@ -739,17 +773,19 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } // Auth canceled - BiometricFrameworkStatsLogger.getInstance().error( - mBiometricContext.updateContext(new OperationContextExt(true /* isBP */), - isCrypto()), - statsModality(), - BiometricsProtoEnums.ACTION_AUTHENTICATE, - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - mDebugEnabled, - latency, - error, - 0 /* vendorCode */, - mUserId); + if (error != 0) { + mBiometricFrameworkStatsLogger.error( + mBiometricContext.updateContext(new OperationContextExt(true /* isBP */), + isCrypto()), + statsModality(), + BiometricsProtoEnums.ACTION_AUTHENTICATE, + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + mDebugEnabled, + latency, + error, + 0 /* vendorCode */, + mUserId); + } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index aa5f9fa96875..7fa4d6ce4d49 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -573,7 +573,7 @@ public class BiometricScheduler { final BiometricSchedulerOperation operation = mCurrentOperation; mHandler.postDelayed(() -> { if (operation == mCurrentOperation) { - Counter.logIncrement("biometric.scheduler_watchdog_triggered_count"); + Counter.logIncrement("biometric.value_scheduler_watchdog_triggered_count"); clearScheduler(); } }, 10000); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index d4bb445d66ac..e85eee817d29 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -309,6 +309,8 @@ public class Vpn { */ @VisibleForTesting static final int DEFAULT_LONG_LIVED_TCP_CONNS_EXPENSIVE_TIMEOUT_SEC = 60; + + private static final int PREFERRED_IKE_PROTOCOL_UNKNOWN = -1; /** * Prefer using {@link IkeSessionParams.ESP_IP_VERSION_AUTO} and * {@link IkeSessionParams.ESP_ENCAP_TYPE_AUTO} for ESP packets. @@ -3643,11 +3645,11 @@ public class Vpn { final int natKeepalive = carrierConfig.getInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT); - final int preferredIpPortocol = - carrierConfig.getInt(KEY_PREFERRED_IKE_PROTOCOL_INT); + final int preferredIpProtocol = carrierConfig.getInt( + KEY_PREFERRED_IKE_PROTOCOL_INT, PREFERRED_IKE_PROTOCOL_UNKNOWN); final String mccMnc = perSubTm.getSimOperator(subId); final CarrierConfigInfo info = - buildCarrierConfigInfo(mccMnc, natKeepalive, preferredIpPortocol); + buildCarrierConfigInfo(mccMnc, natKeepalive, preferredIpProtocol); synchronized (Vpn.this) { mCachedCarrierConfigInfoPerSubId.put(subId, info); } @@ -5010,7 +5012,7 @@ public class Vpn { * Retrieve the VpnProfileState for the profile provisioned by the given package. * * @return the VpnProfileState with current information, or null if there was no profile - * provisioned by the given package. + * provisioned and started by the given package. */ @Nullable public synchronized VpnProfileState getProvisionedVpnProfileState( diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 38445295b0f1..da8eb23cbeae 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -238,6 +238,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6; private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8; + private static final int MSG_RECEIVED_DEVICE_STATE = 9; private static final int[] EMPTY_ARRAY = new int[0]; private static final HdrConversionMode HDR_CONVERSION_MODE_UNSUPPORTED = new HdrConversionMode( HDR_CONVERSION_UNSUPPORTED); @@ -3213,6 +3214,10 @@ public final class DisplayManagerService extends SystemService { mWindowManagerInternal.requestTraversalFromDisplayManager(); break; + case MSG_RECEIVED_DEVICE_STATE: + mWindowManagerInternal.onDisplayManagerReceivedDeviceState(msg.arg1); + break; + case MSG_UPDATE_VIEWPORT: { final boolean changed; synchronized (mSyncRoot) { @@ -4680,6 +4685,14 @@ public final class DisplayManagerService extends SystemService { public void onStateChanged(int deviceState) { boolean isDeviceStateOverrideActive = deviceState != mBaseState; synchronized (mSyncRoot) { + // Notify WindowManager that we are about to handle new device state, this should + // be sent before any work related to the device state in DisplayManager, so + // WindowManager could do implement that depends on the device state and display + // changes (serializes device state update and display change events) + Message msg = mHandler.obtainMessage(MSG_RECEIVED_DEVICE_STATE); + msg.arg1 = deviceState; + mHandler.sendMessage(msg); + mLogicalDisplayMapper .setDeviceStateLocked(deviceState, isDeviceStateOverrideActive); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 2c54e1cea3fa..3eab4b0b1835 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1579,6 +1579,10 @@ public class HdmiControlService extends SystemService { // If the device is not TV, we can't convert path to port-id, so stop here. return true; } + // Invalidate the physical address if parameters length is too short. + if (params.length < offset + 2) { + return false; + } int path = HdmiUtils.twoBytesToInt(params, offset); if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == getPhysicalAddress()) { return true; diff --git a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java index 873d5fc92601..4bc20a555dec 100644 --- a/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java +++ b/services/core/java/com/android/server/hdmi/HdmiEarcLocalDeviceTx.java @@ -42,6 +42,12 @@ public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice { // How long to wait for the audio system to report its capabilities after eARC was connected static final long REPORT_CAPS_MAX_DELAY_MS = 2_000; + // Array containing the names of the eARC states. The integer value of the eARC state + // corresponds to the index in the array. + private static final String earcStatusNames[] = {"HDMI_EARC_STATUS_IDLE", + "HDMI_EARC_STATUS_EARC_PENDING", "HDMI_EARC_STATUS_ARC_PENDING", + "HDMI_EARC_STATUS_EARC_CONNECTED"}; + // eARC Capability Data Structure parameters private static final int EARC_CAPS_PAYLOAD_LENGTH = 0x02; private static final int EARC_CAPS_DATA_START = 0x03; @@ -75,11 +81,17 @@ public class HdmiEarcLocalDeviceTx extends HdmiEarcLocalDevice { mReportCapsRunnable = new ReportCapsRunnable(); } + private String earcStatusToString(int status) { + return earcStatusNames[status]; + } + protected void handleEarcStateChange(@Constants.EarcStatus int status) { int oldEarcStatus; + synchronized (mLock) { - HdmiLogger.debug("eARC state change [old:%b new %b]", mEarcStatus, - status); + HdmiLogger.debug("eARC state change [old: %s(%d) new: %s(%d)]", + earcStatusToString(mEarcStatus), mEarcStatus, + earcStatusToString(status), status); oldEarcStatus = mEarcStatus; mEarcStatus = status; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java index 44ae454e7ef2..c212e8e3c82c 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java @@ -19,6 +19,7 @@ package com.android.server.inputmethod; import static com.android.server.inputmethod.InputMethodManagerService.DEBUG; import static com.android.server.inputmethod.InputMethodUtils.NOT_A_SUBTYPE_ID; +import android.annotation.Nullable; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; @@ -65,8 +66,8 @@ final class InputMethodMenuController { private boolean mShowImeWithHardKeyboard; @GuardedBy("ImfLock.class") - private final InputMethodDialogWindowContext mDialogWindowContext = - new InputMethodDialogWindowContext(); + @Nullable + private InputMethodDialogWindowContext mDialogWindowContext; InputMethodMenuController(InputMethodManagerService service) { mService = service; @@ -124,11 +125,13 @@ final class InputMethodMenuController { } } + if (mDialogWindowContext == null) { + mDialogWindowContext = new InputMethodDialogWindowContext(); + } final Context dialogWindowContext = mDialogWindowContext.get(displayId); mDialogBuilder = new AlertDialog.Builder(dialogWindowContext); mDialogBuilder.setOnCancelListener(dialog -> hideInputMethodMenu()); - // TODO(b/277061090): refactor UI components should not be created while holding a lock. final Context dialogContext = mDialogBuilder.getContext(); final TypedArray a = dialogContext.obtainStyledAttributes(null, com.android.internal.R.styleable.DialogPreference, @@ -196,11 +199,10 @@ final class InputMethodMenuController { attrs.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; attrs.setTitle("Select input method"); w.setAttributes(attrs); - // TODO(b/277062834) decouple/remove dependency on IMMS mService.updateSystemUiLocked(); mService.sendOnNavButtonFlagsChangedLocked(); + mSwitchingDialog.show(); } - mSwitchingDialog.show(); } private boolean isScreenLocked() { @@ -274,7 +276,6 @@ final class InputMethodMenuController { private final int mTextViewResourceId; private final List<ImeSubtypeListItem> mItemsList; public int mCheckedItem; - private ImeSubtypeListAdapter(Context context, int textViewResourceId, List<ImeSubtypeListItem> itemsList, int checkedItem) { super(context, textViewResourceId, itemsList); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 1059c1981ce5..309a99ecdaee 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -1644,6 +1644,26 @@ class MediaRouter2ServiceImpl { } /** + * Notifies the corresponding router that it was successfully registered. + * + * <p>The message sent to the router includes a snapshot of the initial state, including + * known routes and the system {@link RoutingSessionInfo}. + * + * @param currentRoutes All currently known routes, which are filtered according to package + * visibility before being sent to the router. + * @param currentSystemSessionInfo The current system {@link RoutingSessionInfo}. + */ + public void notifyRegistered( + List<MediaRoute2Info> currentRoutes, RoutingSessionInfo currentSystemSessionInfo) { + try { + mRouter.notifyRouterRegistered( + getVisibleRoutes(currentRoutes), currentSystemSessionInfo); + } catch (RemoteException ex) { + Slog.w(TAG, "Failed to notify router registered. Router probably died.", ex); + } + } + + /** * Sends the corresponding router an {@link * android.media.MediaRouter2.RouteCallback#onRoutesUpdated update} for the given {@code * routes}. @@ -2544,12 +2564,7 @@ class MediaRouter2ServiceImpl { return; } - try { - routerRecord.mRouter.notifyRouterRegistered( - currentRoutes, currentSystemSessionInfo); - } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify router registered. Router probably died.", ex); - } + routerRecord.notifyRegistered(currentRoutes, currentSystemSessionInfo); } private static void notifyRoutesUpdatedToRouterRecords( diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index e56eba659d37..d29d9c84501d 100755..100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -119,6 +119,7 @@ import static android.service.notification.NotificationListenerService.TRIM_LIGH import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING; +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; @@ -223,6 +224,8 @@ import android.os.IInterface; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; @@ -234,6 +237,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.VibrationEffect; +import android.os.WorkSource; import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.Settings; @@ -559,6 +563,7 @@ public class NotificationManagerService extends SystemService { private PermissionHelper mPermissionHelper; private UsageStatsManagerInternal mUsageStatsManagerInternal; private TelecomManager mTelecomManager; + private PowerManager mPowerManager; private PostNotificationTrackerFactory mPostNotificationTrackerFactory; final IBinder mForegroundToken = new Binder(); @@ -923,7 +928,7 @@ public class NotificationManagerService extends SystemService { if (oldFlags != flags) { summary.getSbn().getNotification().flags = flags; mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } @@ -1457,7 +1462,7 @@ public class NotificationManagerService extends SystemService { // want to adjust the flag behaviour. mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r, true /* isAppForeground*/, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } } @@ -1488,7 +1493,7 @@ public class NotificationManagerService extends SystemService { mHandler.post( new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r, /* foreground= */ true, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } } @@ -1843,7 +1848,7 @@ public class NotificationManagerService extends SystemService { } } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)) { int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userHandle >= 0) { + if (userHandle >= 0 && !mDpm.isKeepProfilesRunningEnabled()) { cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle, REASON_PROFILE_TURNED_OFF, null); mSnoozeHelper.clearData(userHandle); @@ -2233,7 +2238,7 @@ public class NotificationManagerService extends SystemService { UsageStatsManagerInternal usageStatsManagerInternal, TelecomManager telecomManager, NotificationChannelLogger channelLogger, SystemUiSystemPropertiesFlags.FlagResolver flagResolver, - PermissionManager permissionManager, + PermissionManager permissionManager, PowerManager powerManager, PostNotificationTrackerFactory postNotificationTrackerFactory) { mHandler = handler; Resources resources = getContext().getResources(); @@ -2265,6 +2270,7 @@ public class NotificationManagerService extends SystemService { mDpm = dpm; mUm = userManager; mTelecomManager = telecomManager; + mPowerManager = powerManager; mPostNotificationTrackerFactory = postNotificationTrackerFactory; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -2568,6 +2574,7 @@ public class NotificationManagerService extends SystemService { getContext().getSystemService(TelecomManager.class), new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver(), getContext().getSystemService(PermissionManager.class), + getContext().getSystemService(PowerManager.class), new PostNotificationTrackerFactory() {}); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, @@ -2684,7 +2691,7 @@ public class NotificationManagerService extends SystemService { final boolean isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } @@ -6577,7 +6584,7 @@ public class NotificationManagerService extends SystemService { void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid, final int callingPid, final String tag, final int id, final Notification notification, int incomingUserId, boolean postSilently) { - PostNotificationTracker tracker = mPostNotificationTrackerFactory.newTracker(); + PostNotificationTracker tracker = acquireWakeLockForPost(pkg, callingUid); boolean enqueued = false; try { enqueued = enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, @@ -6589,6 +6596,25 @@ public class NotificationManagerService extends SystemService { } } + private PostNotificationTracker acquireWakeLockForPost(String pkg, int uid) { + if (mFlagResolver.isEnabled(WAKE_LOCK_FOR_POSTING_NOTIFICATION) + && Binder.withCleanCallingIdentity( + () -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, false))) { + // The package probably doesn't have WAKE_LOCK permission and should not require it. + return Binder.withCleanCallingIdentity(() -> { + WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "NotificationManagerService:post:" + pkg); + wakeLock.setWorkSource(new WorkSource(uid, pkg)); + // TODO(b/275044361): Adjust to a more reasonable number when we have the data. + wakeLock.acquire(30_000); + return mPostNotificationTrackerFactory.newTracker(wakeLock); + }); + } else { + return mPostNotificationTrackerFactory.newTracker(null); + } + } + /** * @return True if we successfully processed the notification and handed off the task of * enqueueing it to a background thread; false otherwise. @@ -7106,7 +7132,7 @@ public class NotificationManagerService extends SystemService { mHandler.post( new NotificationManagerService.EnqueueNotificationRunnable( r.getUser().getIdentifier(), r, isAppForeground, - mPostNotificationTrackerFactory.newTracker())); + mPostNotificationTrackerFactory.newTracker(null))); } } } @@ -12168,20 +12194,20 @@ public class NotificationManagerService extends SystemService { } interface PostNotificationTrackerFactory { - default PostNotificationTracker newTracker() { - return new PostNotificationTracker(); + default PostNotificationTracker newTracker(@Nullable WakeLock optionalWakelock) { + return new PostNotificationTracker(optionalWakelock); } } static class PostNotificationTracker { @ElapsedRealtimeLong private final long mStartTime; - @Nullable private NotificationRecordLogger.NotificationReported mReport; + @Nullable private final WakeLock mWakeLock; private boolean mOngoing; @VisibleForTesting - PostNotificationTracker() { - // TODO(b/275044361): (Conditionally) receive a wakelock. + PostNotificationTracker(@Nullable WakeLock wakeLock) { mStartTime = SystemClock.elapsedRealtime(); + mWakeLock = wakeLock; mOngoing = true; if (DBG) { Slog.d(TAG, "PostNotification: Started"); @@ -12199,9 +12225,8 @@ public class NotificationManagerService extends SystemService { } /** - * Cancels the tracker (TODO(b/275044361): releasing the acquired WakeLock). Either - * {@link #finish} or {@link #cancel} (exclusively) should be called on this object before - * it's discarded. + * Cancels the tracker (releasing the acquired WakeLock). Either {@link #finish} or + * {@link #cancel} (exclusively) should be called on this object before it's discarded. */ void cancel() { if (!isOngoing()) { @@ -12209,9 +12234,9 @@ public class NotificationManagerService extends SystemService { return; } mOngoing = false; - - // TODO(b/275044361): Release wakelock. - + if (mWakeLock != null) { + Binder.withCleanCallingIdentity(() -> mWakeLock.release()); + } if (DBG) { long elapsedTime = SystemClock.elapsedRealtime() - mStartTime; Slog.d(TAG, TextUtils.formatSimple("PostNotification: Abandoned after %d ms", @@ -12220,9 +12245,9 @@ public class NotificationManagerService extends SystemService { } /** - * Finishes the tracker (TODO(b/275044361): releasing the acquired WakeLock) and returns the - * time elapsed since the operation started, in milliseconds. Either {@link #finish} or - * {@link #cancel} (exclusively) should be called on this object before it's discarded. + * Finishes the tracker (releasing the acquired WakeLock) and returns the time elapsed since + * the operation started, in milliseconds. Either {@link #finish} or {@link #cancel} + * (exclusively) should be called on this object before it's discarded. */ @DurationMillisLong long finish() { @@ -12232,9 +12257,9 @@ public class NotificationManagerService extends SystemService { return elapsedTime; } mOngoing = false; - - // TODO(b/275044361): Release wakelock. - + if (mWakeLock != null) { + Binder.withCleanCallingIdentity(() -> mWakeLock.release()); + } if (DBG) { Slog.d(TAG, TextUtils.formatSimple("PostNotification: Finished in %d ms", elapsedTime)); diff --git a/services/core/java/com/android/server/notification/TEST_MAPPING b/services/core/java/com/android/server/notification/TEST_MAPPING new file mode 100644 index 000000000000..59b2bc1e4f73 --- /dev/null +++ b/services/core/java/com/android/server/notification/TEST_MAPPING @@ -0,0 +1,49 @@ +{ + "presubmit": [ + { + "name": "CtsNotificationTestCases", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + }, + { + "name": "FrameworksUiServicesTests", + "options": [ + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.LargeTest" + }, + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsNotificationTestCases" + } + ] +} diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 06db5be349d4..50f1673cae44 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -2145,7 +2145,7 @@ final class InstallPackageHelper { final String pkgName = pkg.getPackageName(); final int[] installedForUsers = installRequest.getOriginUsers(); final int installReason = installRequest.getInstallReason(); - final String installerPackageName = installRequest.getSourceInstallerPackageName(); + final String installerPackageName = installRequest.getInstallerPackageName(); if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath()); synchronized (mPm.mLock) { diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java index 95e790450724..34648740d54c 100644 --- a/services/core/java/com/android/server/pm/InstallRequest.java +++ b/services/core/java/com/android/server/pm/InstallRequest.java @@ -366,12 +366,6 @@ final class InstallRequest { public String getApexModuleName() { return mApexModuleName; } - - @Nullable - public String getSourceInstallerPackageName() { - return mInstallArgs.mInstallSource.mInstallerPackageName; - } - public boolean isRollback() { return mInstallArgs != null && mInstallArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK; diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 6491fd1b1f98..a9115371413c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -511,7 +511,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } catch (FileNotFoundException e) { // Missing sessions are okay, probably first boot - } catch (IOException | XmlPullParserException e) { + } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) { Slog.wtf(TAG, "Failed reading install sessions", e); } finally { IoUtils.closeQuietly(fis); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bba8043af5be..db47306ad58e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3409,7 +3409,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService } @Nullable - private String getDevicePolicyManagementRoleHolderPackageName(int userId) { + public String getDevicePolicyManagementRoleHolderPackageName(int userId) { return Binder.withCleanCallingIdentity(() -> { RoleManager roleManager = mContext.getSystemService(RoleManager.class); List<String> roleHolders = diff --git a/services/core/java/com/android/server/pm/ResilientAtomicFile.java b/services/core/java/com/android/server/pm/ResilientAtomicFile.java index 19aa4f8e8d0b..54ca426a6dc3 100644 --- a/services/core/java/com/android/server/pm/ResilientAtomicFile.java +++ b/services/core/java/com/android/server/pm/ResilientAtomicFile.java @@ -230,7 +230,9 @@ final class ResilientAtomicFile implements Closeable { + Log.getStackTraceString(e)); } - mCurrentFile.delete(); + if (!mCurrentFile.delete()) { + throw new IllegalStateException("Failed to remove " + mCurrentFile); + } mCurrentFile = null; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f2d1357ac1b5..aaf13ebeff2a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3714,11 +3714,16 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile if (parser.getName().equals(TAG_PERMISSIONS)) { final LegacyPermissionState legacyState; if (ps.hasSharedUser()) { - legacyState = getSettingLPr(ps.getSharedUserAppId()).getLegacyPermissionState(); + final SettingBase sharedUserSettings = getSettingLPr( + ps.getSharedUserAppId()); + legacyState = sharedUserSettings != null + ? sharedUserSettings.getLegacyPermissionState() : null; } else { legacyState = ps.getLegacyPermissionState(); } - readInstallPermissionsLPr(parser, legacyState, users); + if (legacyState != null) { + readInstallPermissionsLPr(parser, legacyState, users); + } } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) { readUsesStaticLibLPw(parser, ps); } else if (parser.getName().equals(TAG_USES_SDK_LIB)) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index a020728cc85e..5b3514c01f9f 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2547,10 +2547,7 @@ public class ShortcutService extends IShortcutService.Stub { enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, "getShareTargets"); final ComponentName chooser = injectChooserActivity(); - final String pkg = (chooser != null - && mPackageManagerInternal.getComponentEnabledSetting(chooser, - injectBinderCallingUid(), userId) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) - ? chooser.getPackageName() : mContext.getPackageName(); + final String pkg = chooser != null ? chooser.getPackageName() : mContext.getPackageName(); synchronized (mLock) { throwIfUserLockedL(userId); final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>(); diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index ba825774daf6..08934c69e099 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -724,6 +724,10 @@ public final class SuspendPackageHelper { for (PackageInfo info : pkgInfos) { result.add(info.packageName); } + + // Role holder may be null, but ArraySet handles it correctly. + result.remove(mPm.getDevicePolicyManagementRoleHolderPackageName(userId)); + return result; } diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index d108e1487564..d55f85cde5af 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -986,11 +986,14 @@ public class PackageInfoUtils { } /** @see ApplicationInfo#privateFlagsExt */ - public static int appInfoPrivateFlagsExt(int pkgWithoutStateFlags, + private static int appInfoPrivateFlagsExt(int pkgWithoutStateFlags, @Nullable PackageStateInternal pkgSetting) { // @formatter:off - // TODO: Add state specific flags - return pkgWithoutStateFlags; + int flags = pkgWithoutStateFlags; + if (pkgSetting != null) { + flags |= flag(pkgSetting.getCpuAbiOverride() != null, ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE); + } + return flags; // @formatter:on } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 3e7ae331d705..2499529f0fc0 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -141,6 +141,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageState; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.SharedUserApi; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ParsedPermission; import com.android.server.pm.pkg.component.ParsedPermissionGroup; @@ -4538,8 +4539,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt final int appId = ps.getAppId(); final LegacyPermissionState legacyState; if (ps.hasSharedUser()) { - legacyState = mPackageManagerInt.getSharedUserApi( - ps.getSharedUserAppId()).getSharedUserLegacyPermissionState(); + final int sharedUserId = ps.getSharedUserAppId(); + SharedUserApi sharedUserApi = mPackageManagerInt.getSharedUserApi(sharedUserId); + if (sharedUserApi == null) { + Slog.wtf(TAG, "Missing shared user Api for " + sharedUserId); + return; + } + legacyState = sharedUserApi.getSharedUserLegacyPermissionState(); } else { legacyState = ps.getLegacyPermissionState(); } @@ -4584,8 +4590,13 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt ps.setInstallPermissionsFixed(false); final LegacyPermissionState legacyState; if (ps.hasSharedUser()) { - legacyState = mPackageManagerInt.getSharedUserApi( - ps.getSharedUserAppId()).getSharedUserLegacyPermissionState(); + final int sharedUserId = ps.getSharedUserAppId(); + SharedUserApi sharedUserApi = mPackageManagerInt.getSharedUserApi(sharedUserId); + if (sharedUserApi == null) { + Slog.wtf(TAG, "Missing shared user Api for " + sharedUserId); + return; + } + legacyState = sharedUserApi.getSharedUserLegacyPermissionState(); } else { legacyState = ps.getLegacyPermissionState(); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9c780bb90b22..3e81f465a6c2 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -6012,12 +6012,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - @WindowManagerFuncs.LidState - public int getLidState() { - return mDefaultDisplayPolicy.getLidState(); - } - - @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode()); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 3da78123016b..887f9461bdce 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -218,14 +218,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * between it and the policy. */ public interface WindowManagerFuncs { - @IntDef(prefix = { "LID_" }, value = { - LID_ABSENT, - LID_CLOSED, - LID_OPEN, - }) - @Retention(RetentionPolicy.SOURCE) - @interface LidState{} - public static final int LID_ABSENT = -1; public static final int LID_CLOSED = 0; public static final int LID_OPEN = 1; @@ -239,9 +231,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public static final int CAMERA_LENS_COVERED = 1; /** - * Returns a {@link LidState} that describes the current state of the lid switch. + * Returns a code that describes the current state of the lid switch. */ - @LidState public int getLidState(); /** @@ -291,7 +282,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Convert the lid state to a human readable format. */ - static String lidStateToString(@LidState int lid) { + static String lidStateToString(int lid) { switch (lid) { case LID_ABSENT: return "LID_ABSENT"; @@ -1250,11 +1241,4 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * @return {@code true} if the key will be handled globally. */ boolean isGlobalKey(int keyCode); - - /** - * Returns a {@link WindowManagerFuncs.LidState} that describes the current state of - * the lid switch. - */ - @WindowManagerFuncs.LidState - int getLidState(); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 712be36e51c6..a53b831d55c1 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -33,7 +33,6 @@ import static android.os.PowerManagerInternal.isInteractive; import static android.os.PowerManagerInternal.wakefulnessToString; import static com.android.internal.util.LatencyTracker.ACTION_TURN_ON_SCREEN; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import android.annotation.IntDef; import android.annotation.NonNull; @@ -41,13 +40,17 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.ActivityManager; -import android.app.AppOpsManager; import android.app.SynchronousUserSwitchObserver; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; +import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; @@ -63,6 +66,7 @@ import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.BatterySaverPolicyConfig; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; @@ -278,6 +282,18 @@ public final class PowerManagerService extends SystemService */ private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L; + /** + * Apps targeting Android V and above need to define + * {@link android.Manifest.permission#TURN_SCREEN_ON} in their manifest for + * {@link android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP} to have any effect. + * Note that most applications should use {@link android.R.attr#turnScreenOn} or + * {@link android.app.Activity#setTurnScreenOn(boolean)} instead, as this prevents the + * previous foreground app from being resumed first when the screen turns on. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT) + public static final long REQUIRE_TURN_SCREEN_ON_PERMISSION = 216114297L; + /** Reason ID for holding display suspend blocker. */ private static final String HOLDING_DISPLAY_SUSPEND_BLOCKER = "holding display"; @@ -305,8 +321,9 @@ public final class PowerManagerService extends SystemService private final SystemPropertiesWrapper mSystemProperties; private final Clock mClock; private final Injector mInjector; + private final PermissionCheckerWrapper mPermissionCheckerWrapper; + private final PowerPropertiesWrapper mPowerPropertiesWrapper; - private AppOpsManager mAppOpsManager; private LightsManager mLightsManager; private BatteryManagerInternal mBatteryManagerInternal; private DisplayManagerInternal mDisplayManagerInternal; @@ -1030,9 +1047,64 @@ public final class PowerManagerService extends SystemService return new LowPowerStandbyController(context, looper); } - AppOpsManager createAppOpsManager(Context context) { - return context.getSystemService(AppOpsManager.class); + PermissionCheckerWrapper createPermissionCheckerWrapper() { + return PermissionChecker::checkPermissionForDataDelivery; } + + PowerPropertiesWrapper createPowerPropertiesWrapper() { + return new PowerPropertiesWrapper() { + @Override + public boolean waive_target_sdk_check_for_turn_screen_on() { + return PowerProperties.waive_target_sdk_check_for_turn_screen_on().orElse( + false); + } + + @Override + public boolean permissionless_turn_screen_on() { + return PowerProperties.permissionless_turn_screen_on().orElse(false); + } + }; + } + } + + /** Interface for checking an app op permission */ + @VisibleForTesting + interface PermissionCheckerWrapper { + /** + * Checks whether a given data access chain described by the given {@link AttributionSource} + * has a given permission and whether the app op that corresponds to this permission + * is allowed. + * See {@link PermissionChecker#checkPermissionForDataDelivery} for more details. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param pid The process id for which to check. Use {@link PermissionChecker#PID_UNKNOWN} + * if the PID is not known. + * @param attributionSource the permission identity + * @param message A message describing the reason the permission was checked + * @return The permission check result which is any of + * {@link PermissionChecker#PERMISSION_GRANTED}, + * {@link PermissionChecker#PERMISSION_SOFT_DENIED}, + * or {@link PermissionChecker#PERMISSION_HARD_DENIED}. + */ + int checkPermissionForDataDelivery(@NonNull Context context, @NonNull String permission, + int pid, @NonNull AttributionSource attributionSource, @Nullable String message); + } + + /** Interface for querying {@link PowerProperties} */ + @VisibleForTesting + interface PowerPropertiesWrapper { + /** + * Waives the minimum target-sdk check for android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP + * and only allows the flag for apps holding android.permission.TURN_SCREEN_ON + */ + boolean waive_target_sdk_check_for_turn_screen_on(); + + /** + * Allows apps to turn the screen on with android.os.PowerManager#ACQUIRE_CAUSES_WAKEUP + * without being granted android.app.AppOpsManager#OP_TURN_SCREEN_ON. + */ + boolean permissionless_turn_screen_on(); } final Constants mConstants; @@ -1087,8 +1159,8 @@ public final class PowerManagerService extends SystemService Looper.getMainLooper()); mInattentiveSleepWarningOverlayController = mInjector.createInattentiveSleepWarningController(); - - mAppOpsManager = injector.createAppOpsManager(mContext); + mPermissionCheckerWrapper = mInjector.createPermissionCheckerWrapper(); + mPowerPropertiesWrapper = mInjector.createPowerPropertiesWrapper(); mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener(); @@ -1563,19 +1635,29 @@ public final class PowerManagerService extends SystemService } @RequiresPermission(value = android.Manifest.permission.TURN_SCREEN_ON, conditional = true) - private boolean isAcquireCausesWakeupFlagAllowed(String opPackageName, int opUid) { + private boolean isAcquireCausesWakeupFlagAllowed(String opPackageName, int opUid, int opPid) { if (opPackageName == null) { return false; } - if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName) - == AppOpsManager.MODE_ALLOWED) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.TURN_SCREEN_ON) - == PackageManager.PERMISSION_GRANTED) { - Slog.i(TAG, "Allowing device wake-up from app " + opPackageName); - return true; - } + if (PermissionChecker.PERMISSION_GRANTED + == mPermissionCheckerWrapper.checkPermissionForDataDelivery(mContext, + android.Manifest.permission.TURN_SCREEN_ON, opPid, + new AttributionSource(opUid, opPackageName, /* attributionTag= */ null), + /* message= */ "ACQUIRE_CAUSES_WAKEUP for " + opPackageName)) { + Slog.i(TAG, "Allowing device wake-up from app " + opPackageName); + return true; + } + // CompatChanges#isChangeEnabled() returns false for apps with targetSdk < UDC and ensures + // backwards compatibility. + // waive_target_sdk_check_for_turn_screen_on() returns false by default and may be set to + // true on form factors with a more strict policy (e.g. TV) + if (!CompatChanges.isChangeEnabled(REQUIRE_TURN_SCREEN_ON_PERMISSION, opUid) + && !mPowerPropertiesWrapper.waive_target_sdk_check_for_turn_screen_on()) { + Slog.i(TAG, "Allowing device wake-up without android.permission.TURN_SCREEN_ON for " + + opPackageName); + return true; } - if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { + if (mPowerPropertiesWrapper.permissionless_turn_screen_on()) { Slog.d(TAG, "Device wake-up allowed by debug.power.permissionless_turn_screen_on"); return true; } @@ -1590,6 +1672,7 @@ public final class PowerManagerService extends SystemService && isScreenLock(wakeLock)) { String opPackageName; int opUid; + int opPid = PermissionChecker.PID_UNKNOWN; if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) { WorkSource workSource = wakeLock.mWorkSource; WorkChain workChain = getFirstNonEmptyWorkChain(workSource); @@ -1604,10 +1687,12 @@ public final class PowerManagerService extends SystemService } else { opPackageName = wakeLock.mPackageName; opUid = wakeLock.mOwnerUid; + opPid = wakeLock.mOwnerPid; } Integer powerGroupId = wakeLock.getPowerGroupId(); // powerGroupId is null if the wakelock associated display is no longer available - if (powerGroupId != null && isAcquireCausesWakeupFlagAllowed(opPackageName, opUid)) { + if (powerGroupId != null + && isAcquireCausesWakeupFlagAllowed(opPackageName, opUid, opPid)) { if (powerGroupId == Display.INVALID_DISPLAY_GROUP) { // wake up all display groups if (DEBUG_SPEW) { @@ -5734,11 +5819,6 @@ public final class PowerManagerService extends SystemService @Override // Binder call public void wakeUp(long eventTime, @WakeReason int reason, String details, String opPackageName) { - if (mPolicy.getLidState() == LID_CLOSED) { - Slog.d(TAG, "Ignoring wake up call due to the lid being closed"); - return; - } - final long now = mClock.uptimeMillis(); if (eventTime > now) { Slog.e(TAG, "Event time " + eventTime + " cannot be newer than " + now); diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index e82521584731..9128974fa9d3 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1004,7 +1004,8 @@ public class StatsPullAtomService extends SystemService { } private void initAndRegisterDeferredPullers() { - mUwbManager = mContext.getSystemService(UwbManager.class); + mUwbManager = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB) + ? mContext.getSystemService(UwbManager.class) : null; registerUwbActivityInfo(); } @@ -2172,6 +2173,9 @@ public class StatsPullAtomService extends SystemService { } private void registerUwbActivityInfo() { + if (mUwbManager == null) { + return; + } int tagId = FrameworkStatsLog.UWB_ACTIVITY_INFO; mStatsManager.setPullAtomCallback( tagId, diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java index 9ff6a0d7e6ee..d87fca4d3c71 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperData.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java @@ -133,16 +133,14 @@ class WallpaperData { */ final Rect cropHint = new Rect(0, 0, 0, 0); - WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) { - this.userId = userId; - wallpaperFile = new File(wallpaperDir, inputFileName); - cropFile = new File(wallpaperDir, cropFileName); - } - WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) { - this(userId, getWallpaperDir(userId), - (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER, - (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP); + this.userId = userId; + this.mWhich = wallpaperType; + File wallpaperDir = getWallpaperDir(userId); + String wallpaperFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER; + String cropFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP; + this.wallpaperFile = new File(wallpaperDir, wallpaperFileName); + this.cropFile = new File(wallpaperDir, cropFileName); } /** diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index a079875a23e4..9e9b3444006c 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1588,7 +1588,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mShuttingDown = false; mImageWallpaper = ComponentName.unflattenFromString( context.getResources().getString(R.string.image_wallpaper_component)); - mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context); + mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mIPackageManager = AppGlobals.getPackageManager(); diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index bd07622ee5ca..10a2b9717555 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -167,6 +167,9 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, } final TaskSnapshot recordSnapshotInner(TYPE source, boolean allowSnapshotHome) { + if (shouldDisableSnapshots()) { + return null; + } final boolean snapshotHome = allowSnapshotHome && source.isActivityTypeHome(); final TaskSnapshot snapshot = captureSnapshot(source, snapshotHome); if (snapshot == null) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2b2100e56f44..9f16a8441533 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4281,6 +4281,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTaskSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this); mTaskSupervisor.mStoppingActivities.remove(this); + mLetterboxUiController.destroy(); waitingToShow = false; // Defer removal of this activity when either a child is animating, or app transition is on @@ -4350,8 +4351,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); } - mLetterboxUiController.destroy(); - if (!delayed) { updateReportedVisibilityLocked(); } diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index 90af4c6236aa..1eb56f1b7d1c 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -280,6 +280,10 @@ class ActivityStartInterceptor { return false; } + if (isKeepProfilesRunningEnabled() && !isPackageSuspended()) { + return false; + } + IntentSender target = createIntentSenderForOriginalIntent(mCallingUid, FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT); @@ -322,8 +326,7 @@ class ActivityStartInterceptor { private boolean interceptSuspendedPackageIfNeeded() { // Do not intercept if the package is not suspended - if (mAInfo == null || mAInfo.applicationInfo == null || - (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) { + if (!isPackageSuspended()) { return false; } final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked(); @@ -467,6 +470,17 @@ class ActivityStartInterceptor { return true; } + private boolean isPackageSuspended() { + return mAInfo != null && mAInfo.applicationInfo != null + && (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) != 0; + } + + private static boolean isKeepProfilesRunningEnabled() { + DevicePolicyManagerInternal dpmi = + LocalServices.getService(DevicePolicyManagerInternal.class); + return dpmi == null || dpmi.isKeepProfilesRunningEnabled(); + } + /** * Called when an activity is successfully launched. */ diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 0ebec11ea36f..a0a45e67e77b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1573,8 +1573,10 @@ class ActivityStarter { // existence change. transitionController.collectExistenceChange(started); } else if (result == START_DELIVERED_TO_TOP && newTransition != null - // An activity has changed order/visibility so this isn't just deliver-to-top - && mMovedToTopActivity == null) { + // An activity has changed order/visibility or the task is occluded by a transient + // activity, so this isn't just deliver-to-top + && mMovedToTopActivity == null + && !transitionController.isTransientHide(startedActivityRootTask)) { // We just delivered to top, so there isn't an actual transition here. if (!forceTransientTransition) { newTransition.abort(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a650d7709b67..47b51ac164b9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2012,7 +2012,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final DisplayContent dc = mRootWindowContainer.getDisplayContent(displayId); if (dc == null) return; - final Task task = dc.getTask((t) -> t.isLeafTask() && t.isFocusable(), + final Task task = dc.getTask((t) -> t.isLeafTask() && t.isTopActivityFocusable(), true /* traverseTopToBottom */); if (task == null) return; setFocusedTask(task.mTaskId, null /* touchedActivity */); @@ -4505,8 +4505,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // to set debug system properties. To ensure that system properties are set // only when allowed, we check the current UID. if (Process.myUid() == Process.SYSTEM_UID) { - SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc)); - SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc)); + if (values.mcc != 0) { + SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc)); + } + if (values.mnc != 0) { + SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc)); + } } if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) { diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java index 2df601fd1d3b..b9f6e177e637 100644 --- a/services/core/java/com/android/server/wm/AnrController.java +++ b/services/core/java/com/android/server/wm/AnrController.java @@ -68,7 +68,7 @@ class AnrController { void notifyAppUnresponsive(InputApplicationHandle applicationHandle, TimeoutRecord timeoutRecord) { try { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyAppUnresponsive()"); + timeoutRecord.mLatencyTracker.notifyAppUnresponsiveStarted(); timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowStarted(); preDumpIfLockTooSlow(); timeoutRecord.mLatencyTracker.preDumpIfLockTooSlowEnded(); @@ -111,7 +111,6 @@ class AnrController { if (!blamePendingFocusRequest) { Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: " + timeoutRecord.mReason); - dumpAnrStateAsync(activity, null /* windowState */, timeoutRecord.mReason); mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity); } } @@ -123,8 +122,13 @@ class AnrController { } else { activity.inputDispatchingTimedOut(timeoutRecord, INVALID_PID); } + + if (!blamePendingFocusRequest) { + dumpAnrStateAsync(activity, null /* windowState */, timeoutRecord.mReason); + } + } finally { - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + timeoutRecord.mLatencyTracker.notifyAppUnresponsiveEnded(); } } @@ -139,7 +143,7 @@ class AnrController { void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid, @NonNull TimeoutRecord timeoutRecord) { try { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyWindowUnresponsive()"); + timeoutRecord.mLatencyTracker.notifyWindowUnresponsiveStarted(); if (notifyWindowUnresponsive(token, timeoutRecord)) { return; } @@ -150,7 +154,7 @@ class AnrController { } notifyWindowUnresponsive(pid.getAsInt(), timeoutRecord); } finally { - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + timeoutRecord.mLatencyTracker.notifyWindowUnresponsiveEnded(); } } @@ -168,6 +172,7 @@ class AnrController { final int pid; final boolean aboveSystem; final ActivityRecord activity; + final WindowState windowState; timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted(); synchronized (mService.mGlobalLock) { timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded(); @@ -175,7 +180,7 @@ class AnrController { if (target == null) { return false; } - WindowState windowState = target.getWindowState(); + windowState = target.getWindowState(); pid = target.getPid(); // Blame the activity if the input token belongs to the window. If the target is // embedded, then we will blame the pid instead. @@ -183,13 +188,13 @@ class AnrController { ? windowState.mActivityRecord : null; Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + timeoutRecord.mReason); aboveSystem = isWindowAboveSystem(windowState); - dumpAnrStateAsync(activity, windowState, timeoutRecord.mReason); } if (activity != null) { activity.inputDispatchingTimedOut(timeoutRecord, pid); } else { mService.mAmInternal.inputDispatchingTimedOut(pid, aboveSystem, timeoutRecord); } + dumpAnrStateAsync(activity, windowState, timeoutRecord.mReason); return true; } @@ -199,15 +204,10 @@ class AnrController { private void notifyWindowUnresponsive(int pid, TimeoutRecord timeoutRecord) { Slog.i(TAG_WM, "ANR in input window owned by pid=" + pid + ". Reason: " + timeoutRecord.mReason); - timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted(); - synchronized (mService.mGlobalLock) { - timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded(); - dumpAnrStateAsync(null /* activity */, null /* windowState */, timeoutRecord.mReason); - } - // We cannot determine the z-order of the window, so place the anr dialog as high // as possible. mService.mAmInternal.inputDispatchingTimedOut(pid, true /*aboveSystem*/, timeoutRecord); + dumpAnrStateAsync(null /* activity */, null /* windowState */, timeoutRecord.mReason); } /** diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index 123a74dbf597..f7ccc0d91969 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -23,6 +23,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.os.Build; import android.os.Handler; @@ -178,6 +179,14 @@ class AppWarnings { * @param r activity record for which the warning may be displayed */ public void showDeprecatedAbiDialogIfNeeded(ActivityRecord r) { + final boolean isUsingAbiOverride = (r.info.applicationInfo.privateFlagsExt + & ApplicationInfo.PRIVATE_FLAG_EXT_CPU_OVERRIDE) != 0; + if (isUsingAbiOverride) { + // The abiOverride flag was specified during installation, which means that if the app + // is currently running in 32-bit mode, it is intended. Do not show the warning dialog. + return; + } + // The warning dialog can also be disabled for debugging purpose final boolean disableDeprecatedAbiDialog = SystemProperties.getBoolean( "debug.wm.disable_deprecated_abi_dialog", false); if (disableDeprecatedAbiDialog) { diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 7e783938d30a..01158779c24f 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -185,6 +185,8 @@ class AsyncRotationController extends FadeAnimationController implements Consume } } else if (navigationBarCanMove || mTransitionOp == OP_CHANGE_MAY_SEAMLESS) { action = Operation.ACTION_SEAMLESS; + } else if (mDisplayContent.mTransitionController.mNavigationBarAttachedToApp) { + return; } mTargetWindowTokens.put(w.mToken, new Operation(action)); return; diff --git a/services/core/java/com/android/server/wm/DeviceStateController.java b/services/core/java/com/android/server/wm/DeviceStateController.java index 41c052d95053..db4762e5f877 100644 --- a/services/core/java/com/android/server/wm/DeviceStateController.java +++ b/services/core/java/com/android/server/wm/DeviceStateController.java @@ -19,9 +19,6 @@ package com.android.server.wm; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.content.Context; -import android.hardware.devicestate.DeviceStateManager; -import android.os.Handler; -import android.os.HandlerExecutor; import android.util.ArrayMap; import com.android.internal.R; @@ -36,17 +33,15 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * Class that registers callbacks with the {@link DeviceStateManager} and responds to device + * Class that listens for a callback from display manager and responds to device state * changes. */ -final class DeviceStateController implements DeviceStateManager.DeviceStateCallback { +final class DeviceStateController { // Used to synchronize WindowManager services call paths with DeviceStateManager's callbacks. @NonNull private final WindowManagerGlobalLock mWmLock; @NonNull - private final DeviceStateManager mDeviceStateManager; - @NonNull private final int[] mOpenDeviceStates; @NonNull private final int[] mHalfFoldedDeviceStates; @@ -77,10 +72,8 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb CONCURRENT, } - DeviceStateController(@NonNull Context context, @NonNull Handler handler, - @NonNull WindowManagerGlobalLock wmLock) { + DeviceStateController(@NonNull Context context, @NonNull WindowManagerGlobalLock wmLock) { mWmLock = wmLock; - mDeviceStateManager = context.getSystemService(DeviceStateManager.class); mOpenDeviceStates = context.getResources() .getIntArray(R.array.config_openDeviceStates); @@ -97,10 +90,6 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb mMatchBuiltInDisplayOrientationToDefaultDisplay = context.getResources() .getBoolean(R.bool .config_matchSecondaryInternalDisplaysOrientationToReverseDefaultDisplay); - - if (mDeviceStateManager != null) { - mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this); - } } /** @@ -122,9 +111,13 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb } /** - * @return true if the rotation direction on the Z axis should be reversed. + * @return true if the rotation direction on the Z axis should be reversed for the default + * display. */ - boolean shouldReverseRotationDirectionAroundZAxis() { + boolean shouldReverseRotationDirectionAroundZAxis(@NonNull DisplayContent displayContent) { + if (!displayContent.isDefaultDisplay) { + return false; + } return ArrayUtils.contains(mReverseRotationAroundZAxisStates, mCurrentState); } @@ -137,8 +130,19 @@ final class DeviceStateController implements DeviceStateManager.DeviceStateCallb return mMatchBuiltInDisplayOrientationToDefaultDisplay; } - @Override - public void onStateChanged(int state) { + /** + * This event is sent from DisplayManager just before the device state is applied to + * the displays. This is needed to make sure that we first receive this callback before + * any device state related display updates from the DisplayManager. + * + * The flow for this event is the following: + * - {@link DeviceStateManager} sends event to {@link android.hardware.display.DisplayManager} + * - {@link android.hardware.display.DisplayManager} sends it to {@link WindowManagerInternal} + * - {@link WindowManagerInternal} eventually calls this method + * + * @param state device state as defined by {@link DeviceStateManager} + */ + public void onDeviceStateReceivedByDisplayManager(int state) { mCurrentState = state; final DeviceState deviceState; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 6aec96988a77..7fc86b0fdc01 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5602,17 +5602,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit, @WindowManager.TransitionFlags int flags) { - prepareAppTransition(transit, flags); - mTransitionController.requestTransitionIfNeeded(transit, flags, - null /* trigger */, this); + requestTransitionAndLegacyPrepare(transit, flags, null /* trigger */); } /** @see #requestTransitionAndLegacyPrepare(int, int) */ void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit, - @Nullable WindowContainer trigger) { - prepareAppTransition(transit); - mTransitionController.requestTransitionIfNeeded(transit, 0 /* flags */, - trigger, this); + @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) { + prepareAppTransition(transit, flags); + mTransitionController.requestTransitionIfNeeded(transit, flags, trigger, this); } void executeAppTransition() { diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index da102d0713ae..65771d1ce373 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -225,7 +225,6 @@ public class DisplayPolicy { /** Currently it can only be non-null when physical display switch happens. */ private DecorInsets.Cache mCachedDecorInsets; - @WindowManagerFuncs.LidState private volatile int mLidState = LID_ABSENT; private volatile int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED; private volatile boolean mHdmiPlugged; @@ -753,11 +752,10 @@ public class DisplayPolicy { return mNavigationBarCanMove; } - public void setLidState(@WindowManagerFuncs.LidState int lidState) { + public void setLidState(int lidState) { mLidState = lidState; } - @WindowManagerFuncs.LidState public int getLidState() { return mLidState; } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index bc1ddf8f66bc..b681c198538f 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -950,7 +950,7 @@ public class DisplayRotation { } void freezeRotation(int rotation) { - if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) { + if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) { rotation = RotationUtils.reverseRotationDirectionAroundZAxis(rotation); } @@ -1225,7 +1225,7 @@ public class DisplayRotation { if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) { sensorRotation = -1; } - if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) { + if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mDisplayContent)) { sensorRotation = RotationUtils.reverseRotationDirectionAroundZAxis(sensorRotation); } mLastSensorRotation = sensorRotation; @@ -1730,7 +1730,9 @@ public class DisplayRotation { } /** - * Called by the DeviceStateManager callback when the device state changes. + * Called by the display manager just before it applied the device state, it is guaranteed + * that in case of physical display change the {@link DisplayRotation#physicalDisplayChanged} + * method will be invoked *after* this one. */ void foldStateChanged(DeviceStateController.DeviceState deviceState) { if (mFoldController != null) { diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index 1fbf59301191..2b34bb22729d 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -42,6 +42,7 @@ import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.pm.ActivityInfo.ScreenOrientation; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.hardware.camera2.CameraManager; import android.os.Handler; @@ -423,7 +424,18 @@ final class DisplayRotationCompatPolicy { // for the activity embedding case. if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW && isTreatmentEnabledForActivity(topActivity, /* mustBeFullscreen */ false)) { - showToast(R.string.display_rotation_camera_compat_toast_in_split_screen); + final PackageManager packageManager = mWmService.mContext.getPackageManager(); + try { + showToast( + R.string.display_rotation_camera_compat_toast_in_multi_window, + (String) packageManager.getApplicationLabel( + packageManager.getApplicationInfo(packageName, /* flags */ 0))); + } catch (PackageManager.NameNotFoundException e) { + ProtoLog.e(WM_DEBUG_ORIENTATION, + "DisplayRotationCompatPolicy: Multi-window toast not shown as " + + "package '%s' cannot be found.", + packageName); + } } } } @@ -434,6 +446,15 @@ final class DisplayRotationCompatPolicy { () -> Toast.makeText(mWmService.mContext, stringRes, Toast.LENGTH_LONG).show()); } + @VisibleForTesting + void showToast(@StringRes int stringRes, @NonNull String applicationLabel) { + UiThread.getHandler().post( + () -> Toast.makeText( + mWmService.mContext, + mWmService.mContext.getString(stringRes, applicationLabel), + Toast.LENGTH_LONG).show()); + } + private synchronized void notifyCameraClosed(@NonNull String cameraId) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Display id=%d is notified that Camera %s is closed, scheduling rotation update.", diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index b7eaf259ea7a..5e2618b00e2e 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -272,15 +272,17 @@ class InsetsSourceProvider { /** @return A new source computed by the specified window frame in the given display frames. */ InsetsSource createSimulatedSource(DisplayFrames displayFrames, Rect frame) { - // Don't copy visible frame because it might not be calculated in the provided display - // frames and it is not significant for this usage. - final InsetsSource source = new InsetsSource(mSource.getId(), mSource.getType()); - source.setVisible(mSource.isVisible()); + final InsetsSource source = new InsetsSource(mSource); mTmpRect.set(frame); if (mFrameProvider != null) { mFrameProvider.apply(displayFrames, mWindowContainer, mTmpRect); } source.setFrame(mTmpRect); + + // Don't copy visible frame because it might not be calculated in the provided display + // frames and it is not significant for this usage. + source.setVisibleFrame(null); + return source; } @@ -452,7 +454,7 @@ class InsetsSourceProvider { if (mSource.getType() == WindowInsets.Type.ime()) { setClientVisible(target.isRequestedVisible(WindowInsets.Type.ime())); } - final Transaction t = mDisplayContent.getSyncTransaction(); + final Transaction t = mWindowContainer.getSyncTransaction(); mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */, ANIMATION_TYPE_INSETS_CONTROL); diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 99878a3d0ffe..ad9c3b274267 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -19,17 +19,21 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; @@ -171,10 +175,11 @@ class KeyguardController { final KeyguardDisplayState state = getDisplayState(displayId); final boolean aodChanged = aodShowing != state.mAodShowing; final boolean aodRemoved = state.mAodShowing && !aodShowing; + final boolean goingAwayRemoved = state.mKeyguardGoingAway && keyguardShowing; // If keyguard is going away, but SystemUI aborted the transition, need to reset state. // Do not reset keyguardChanged status when only AOD is removed. final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing) - || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved); + || (goingAwayRemoved && !aodRemoved); if (aodRemoved) { updateDeferTransitionForAod(false /* waiting */); } @@ -214,6 +219,15 @@ class KeyguardController { if (keyguardShowing) { state.mDismissalRequested = false; } + if (goingAwayRemoved) { + // Keyguard dismiss is canceled. Send a transition to undo the changes and clean up + // before holding the sleep token again. + final DisplayContent dc = mRootWindowContainer.getDefaultDisplay(); + dc.requestTransitionAndLegacyPrepare( + TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING); + dc.mWallpaperController.showWallpaperInTransition(false /* showHome */); + mWindowManager.executeAppTransition(); + } } // Update the sleep token first such that ensureActivitiesVisible has correct sleep token @@ -269,7 +283,7 @@ class KeyguardController { updateKeyguardSleepToken(); // Make the home wallpaper visible - dc.mWallpaperController.showHomeWallpaperInTransition(); + dc.mWallpaperController.showWallpaperInTransition(true /* showHome */); // Some stack visibility might change (e.g. docked stack) mRootWindowContainer.resumeFocusedTasksTopActivities(); mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); @@ -413,10 +427,12 @@ class KeyguardController { if (isDisplayOccluded(DEFAULT_DISPLAY)) { mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare( TRANSIT_KEYGUARD_OCCLUDE, + TRANSIT_FLAG_KEYGUARD_OCCLUDING, topActivity != null ? topActivity.getRootTask() : null); } else { mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare( - TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */); + TRANSIT_KEYGUARD_UNOCCLUDE, + TRANSIT_FLAG_KEYGUARD_UNOCCLUDING); } updateKeyguardSleepToken(DEFAULT_DISPLAY); mWindowManager.executeAppTransition(); diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java index 78522498987c..f0757db76bda 100644 --- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java +++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java @@ -71,7 +71,10 @@ public class PhysicalDisplaySwitchTransitionLauncher { } /** - * Called by the DeviceStateManager callback when the state changes. + * Called by the display manager just before it applied the device state, it is guaranteed + * that in case of physical display change the + * {@link PhysicalDisplaySwitchTransitionLauncher#requestDisplaySwitchTransitionIfNeeded} + * method will be invoked *after* this one. */ void foldStateChanged(DeviceState newDeviceState) { boolean isUnfolding = mDeviceState == FOLDED diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 7e839596c59b..bc7fa31125f9 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -438,8 +438,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mTaskSupervisor = mService.mTaskSupervisor; mTaskSupervisor.mRootWindowContainer = this; mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG); - mDeviceStateController = new DeviceStateController(service.mContext, service.mH, - service.mGlobalLock); + mDeviceStateController = new DeviceStateController(service.mContext, service.mGlobalLock); mDisplayRotationCoordinator = new DisplayRotationCoordinator(); } @@ -1283,6 +1282,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> false /* includingParents */); } + /** + * Called just before display manager has applied the device state to the displays + * @param deviceState device state as defined by + * {@link android.hardware.devicestate.DeviceStateManager} + */ + void onDisplayManagerReceivedDeviceState(int deviceState) { + mDeviceStateController.onDeviceStateReceivedByDisplayManager(deviceState); + } + // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. DisplayContent getDefaultDisplay() { return mDefaultDisplay; @@ -2349,10 +2357,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */, display.mTransitionController, mWmService.mSyncEngine); final TransitionController.OnStartCollect sendSleepTransition = (deferred) -> { - display.mTransitionController.requestStartTransition(transition, - null /* trigger */, null /* remote */, null /* display */); - // Force playing immediately so that unrelated ops can't be collected. - transition.playNow(); + if (deferred && !display.shouldSleep()) { + transition.abort(); + } else { + display.mTransitionController.requestStartTransition(transition, + null /* trigger */, null /* remote */, null /* display */); + // Force playing immediately so that unrelated ops can't be collected. + transition.playNow(); + } }; if (!display.mTransitionController.isCollecting()) { // Since this bypasses sync, submit directly ignoring whether sync-engine diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index afef85e34fe0..58e1c544202d 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -314,6 +314,11 @@ class SnapshotPersistQueue { } final Bitmap swBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false /* isMutable */); + if (swBitmap == null) { + Slog.e(TAG, "Bitmap conversion from (config=" + bitmap.getConfig() + ", isMutable=" + + bitmap.isMutable() + ") to (config=ARGB_8888, isMutable=false) failed."); + return false; + } final File file = mPersistInfoProvider.getHighResolutionBitmapFile(mId, mUserId); try { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index bb6f8056acda..ceb80d6a1a8b 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3290,6 +3290,12 @@ class Task extends TaskFragment { scheduleAnimation(); } + // Let organizer manage task visibility for shell transition. So don't change it's + // visibility during collecting. + if (mTransitionController.isCollecting() && mCreatedByOrganizer) { + return; + } + // We intend to let organizer manage task visibility but it doesn't // have enough information until we finish shell transitions. // In the mean time we do an easy fix here. @@ -3437,6 +3443,7 @@ class Task extends TaskFragment { : INVALID_TASK_ID; info.isFocused = isFocused(); info.isVisible = hasVisibleChildren(); + info.isVisibleRequested = isVisibleRequested(); info.isSleeping = shouldSleepActivities(); info.isLetterboxDoubleTapEnabled = top != null && top.mLetterboxUiController.isLetterboxDoubleTapEducationEnabled(); @@ -5687,17 +5694,28 @@ class Task extends TaskFragment { } private boolean moveTaskToBackInner(@NonNull Task task) { - moveToBack("moveTaskToBackInner", task); - - if (inPinnedWindowingMode()) { - mTaskSupervisor.removeRootTask(this); - return true; + if (mTransitionController.isShellTransitionsEnabled()) { + // Preventing from update surface position for WindowState if configuration changed, + // because the position is depends on WindowFrame, so update the position before + // relayout will only update it to "old" position. + mAtmService.deferWindowLayout(); } + try { + moveToBack("moveTaskToBackInner", task); - mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, - mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */, - false /* deferResume */); + if (inPinnedWindowingMode()) { + mTaskSupervisor.removeRootTask(this); + return true; + } + mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, + mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */, + false /* deferResume */); + } finally { + if (mTransitionController.isShellTransitionsEnabled()) { + mAtmService.continueWindowLayout(); + } + } ActivityRecord topActivity = getDisplayArea().topRunningActivity(); Task topRootTask = topActivity.getRootTask(); if (topRootTask != null && topRootTask != this && topActivity.isState(RESUMED)) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index c6c3b14bf98b..0c1f33ccedbc 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1017,8 +1017,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (isTopActivityLaunchedBehind()) { return TASK_FRAGMENT_VISIBILITY_VISIBLE; } + final WindowContainer<?> parent = getParent(); final Task thisTask = asTask(); - if (thisTask != null && mTransitionController.isTransientHide(thisTask)) { + if (thisTask != null && parent.asTask() == null + && mTransitionController.isTransientHide(thisTask)) { + // Keep transient-hide root tasks visible. Non-root tasks still follow standard rule. return TASK_FRAGMENT_VISIBILITY_VISIBLE; } @@ -1028,7 +1031,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { // This TaskFragment is only considered visible if all its parent TaskFragments are // considered visible, so check the visibility of all ancestor TaskFragment first. - final WindowContainer parent = getParent(); if (parent.asTaskFragment() != null) { final int parentVisibility = parent.asTaskFragment().getVisibility(starting); if (parentVisibility == TASK_FRAGMENT_VISIBILITY_INVISIBLE) { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index b938b9e4ec51..aad12251502d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -52,7 +52,6 @@ import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_NO_ANIMATION; -import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_TASK_LAUNCHING_BEHIND; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; @@ -235,9 +234,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { private @TransitionState int mState = STATE_PENDING; private final ReadyTracker mReadyTracker = new ReadyTracker(); - // TODO(b/188595497): remove when not needed. - /** @see RecentsAnimationController#mNavigationBarAttachedToApp */ - private boolean mNavBarAttachedToApp = false; private int mRecentsDisplayId = INVALID_DISPLAY; /** The delay for light bar appearance animation. */ @@ -691,6 +687,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (!wc.mDisplayContent.getDisplayPolicy().isScreenOnFully() || wc.mDisplayContent.getDisplayInfo().state == Display.STATE_OFF) { mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE; + return; } if (mContainerFreezer == null) { @@ -1191,7 +1188,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // processed all the participants first (in particular, we want to trigger pip-enter first) for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); - if (ar != null) { + // If the activity was just inserted to an invisible task, it will keep INITIALIZING + // state. Then no need to notify the callback to avoid clearing some states + // unexpectedly, e.g. launch-task-behind. + if (ar != null && (ar.isVisibleRequested() + || !ar.isState(ActivityRecord.State.INITIALIZING))) { mController.dispatchLegacyAppTransitionFinished(ar); } } @@ -1777,7 +1778,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (navWindow == null || navWindow.mToken == null) { return; } - mNavBarAttachedToApp = true; + mController.mNavigationBarAttachedToApp = true; navWindow.mToken.cancelAnimation(); final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); @@ -1799,8 +1800,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { /** @see RecentsAnimationController#restoreNavigationBarFromApp */ void legacyRestoreNavigationBarFromApp() { - if (!mNavBarAttachedToApp) return; - mNavBarAttachedToApp = false; + if (!mController.mNavigationBarAttachedToApp) { + return; + } + mController.mNavigationBarAttachedToApp = false; if (mRecentsDisplayId == INVALID_DISPLAY) { Slog.e(TAG, "Reparented navigation bar without a valid display"); @@ -1833,6 +1836,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { break; } + final AsyncRotationController asyncRotationController = dc.getAsyncRotationController(); + if (asyncRotationController != null) { + asyncRotationController.accept(navWindow); + } + if (animate) { final NavBarFadeAnimationController controller = new NavBarFadeAnimationController(dc); @@ -1841,6 +1849,9 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Reparent the SurfaceControl of nav bar token back. t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); } + + // To apply transactions. + dc.mWmService.scheduleAnimationLocked(); } private void reportStartReasonsToLogger() { @@ -2245,11 +2256,17 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, wc); // Make leash based on highest (z-order) direct child of ancestor with a participant. + // Check whether the ancestor is belonged to last parent, shouldn't happen. + final boolean hasReparent = !wc.isDescendantOf(ancestor); WindowContainer leashReference = wc; - while (leashReference.getParent() != ancestor) { - leashReference = leashReference.getParent(); + if (hasReparent) { + Slog.e(TAG, "Did not find common ancestor! Ancestor= " + ancestor + + " target= " + wc); + } else { + while (leashReference.getParent() != ancestor) { + leashReference = leashReference.getParent(); + } } - final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( "Transition Root: " + leashReference.getName()).build(); rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots"); @@ -2308,23 +2325,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { task.fillTaskInfo(tinfo); change.setTaskInfo(tinfo); change.setRotationAnimation(getTaskRotationAnimation(task)); - final ActivityRecord topMostActivity = task.getTopMostActivity(); - change.setAllowEnterPip(topMostActivity != null - && topMostActivity.checkEnterPictureInPictureAppOpsState()); final ActivityRecord topRunningActivity = task.topRunningActivity(); - if (topRunningActivity != null && task.mDisplayContent != null - // Display won't be rotated for multi window Task, so the fixed rotation - // won't be applied. This can happen when the windowing mode is changed - // before the previous fixed rotation is applied. - && (!task.inMultiWindowMode() || !topRunningActivity.inMultiWindowMode())) { - // If Activity is in fixed rotation, its will be applied with the next rotation, - // when the Task is still in the previous rotation. - final int taskRotation = task.getWindowConfiguration().getDisplayRotation(); - final int activityRotation = topRunningActivity.getWindowConfiguration() - .getDisplayRotation(); - if (taskRotation != activityRotation) { - change.setEndFixedRotation(activityRotation); + if (topRunningActivity != null) { + if (topRunningActivity.info.supportsPictureInPicture()) { + change.setAllowEnterPip( + topRunningActivity.checkEnterPictureInPictureAppOpsState()); } + setEndFixedRotationIfNeeded(change, task, topRunningActivity); } } else if ((info.mFlags & ChangeInfo.FLAG_SEAMLESS_ROTATION) != 0) { change.setRotationAnimation(ROTATION_ANIMATION_SEAMLESS); @@ -2432,6 +2439,48 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } return animOptions; } + + private static void setEndFixedRotationIfNeeded(@NonNull TransitionInfo.Change change, + @NonNull Task task, @NonNull ActivityRecord taskTopRunning) { + if (!taskTopRunning.isVisibleRequested()) { + // Fixed rotation only applies to opening or changing activity. + return; + } + if (task.inMultiWindowMode() && taskTopRunning.inMultiWindowMode()) { + // Display won't be rotated for multi window Task, so the fixed rotation won't be + // applied. This can happen when the windowing mode is changed before the previous + // fixed rotation is applied. Check both task and activity because the activity keeps + // fullscreen mode when the task is entering PiP. + return; + } + final int taskRotation = task.getWindowConfiguration().getDisplayRotation(); + final int activityRotation = taskTopRunning.getWindowConfiguration() + .getDisplayRotation(); + // If the Activity uses fixed rotation, its rotation will be applied to display after + // the current transition is done, while the Task is still in the previous rotation. + if (taskRotation != activityRotation) { + change.setEndFixedRotation(activityRotation); + return; + } + + // For example, the task is entering PiP so it no longer decides orientation. If the next + // orientation source (it could be an activity which was behind the PiP or launching to top) + // will change display rotation, then set the fixed rotation hint as well so the animation + // can consider the rotated position. + if (!task.inPinnedWindowingMode() || taskTopRunning.mDisplayContent.inTransition()) { + return; + } + final WindowContainer<?> orientationSource = + taskTopRunning.mDisplayContent.getLastOrientationSource(); + if (orientationSource == null) { + return; + } + final int nextRotation = orientationSource.getWindowConfiguration().getDisplayRotation(); + if (taskRotation != nextRotation) { + change.setEndFixedRotation(nextRotation); + } + } + /** * Finds the top-most common ancestor of app targets. * @@ -2454,6 +2503,20 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Skip the non-app window or windows on a different display continue; } + // Re-initiate the last parent as the initial ancestor instead of the top target. + // When move a leaf task from organized task to display area, try to keep the transition + // root be the original organized task for close transition animation. + // Otherwise, shell will use wrong root layer to play animation. + // Note: Since the target is sorted, so only need to do this at the lowest target. + if (change.mStartParent != null && wc.getParent() != null + && change.mStartParent.isAttached() && wc.getParent() != change.mStartParent + && i == targets.size() - 1) { + final int transitionMode = change.getTransitMode(wc); + if (transitionMode == TRANSIT_CLOSE || transitionMode == TRANSIT_TO_BACK) { + ancestor = change.mStartParent; + continue; + } + } while (!wc.isDescendantOf(ancestor)) { ancestor = ancestor.getParent(); } @@ -2820,9 +2883,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } } - if (occludesKeyguard(wc)) { - flags |= FLAG_OCCLUDES_KEYGUARD; - } if ((mFlags & FLAG_CHANGE_NO_ANIMATION) != 0 && (mFlags & FLAG_CHANGE_YES_ANIMATION) == 0) { flags |= FLAG_NO_ANIMATION; diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 1c6a412e5450..359b353ba336 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -16,10 +16,10 @@ package com.android.server.wm; +import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; @@ -206,6 +206,11 @@ class TransitionController { */ boolean mBuildingFinishLayers = false; + /** + * Whether the surface of navigation bar token is reparented to an app. + */ + boolean mNavigationBarAttachedToApp = false; + private boolean mAnimatingState = false; final Handler mLoggerHandler = FgThread.getHandler(); @@ -619,9 +624,9 @@ class TransitionController { } // Make the collecting transition wait until this request is ready. mCollectingTransition.setReady(readyGroupRef, false); - if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { - // Add keyguard flag to dismiss keyguard - mCollectingTransition.addFlag(flags); + if ((flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) { + // Add keyguard flags to affect keyguard visibility + mCollectingTransition.addFlag(flags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS); } } else { newTransition = requestStartTransition(createTransition(type, flags), diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 20ce98ca2fae..7f41ff1a3d9b 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -339,12 +339,12 @@ class WallpaperController { } /** - * Change the visibility if wallpaper is home screen only. + * Make one wallpaper visible, according to {@attr showHome}. * This is called during the keyguard unlocking transition - * (see {@link KeyguardController#keyguardGoingAway(int, int)}) and thus assumes that if the - * system wallpaper is shared with lock, then it needs no animation. + * (see {@link KeyguardController#keyguardGoingAway(int, int)}), + * or when a keyguard unlock is cancelled (see {@link KeyguardController}) */ - public void showHomeWallpaperInTransition() { + public void showWallpaperInTransition(boolean showHome) { updateWallpaperWindowsTarget(mFindResults); if (!mFindResults.hasTopShowWhenLockedWallpaper()) { @@ -357,9 +357,9 @@ class WallpaperController { // Shared wallpaper, ensure its visibility showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindows(true); } else { - // Separate lock and home wallpapers: show home wallpaper and hide lock - hideWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(true); - showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(false); + // Separate lock and home wallpapers: show the correct wallpaper in transition + hideWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(showHome); + showWhenLocked.mToken.asWallpaperToken().updateWallpaperWindowsInTransition(!showHome); } } @@ -401,6 +401,19 @@ class WallpaperController { // swiping through Launcher pages). final Rect wallpaperFrame = wallpaperWin.getFrame(); + final int diffWidth = wallpaperFrame.width() - lastWallpaperBounds.width(); + final int diffHeight = wallpaperFrame.height() - lastWallpaperBounds.height(); + if ((wallpaperWin.mAttrs.flags & WindowManager.LayoutParams.FLAG_SCALED) != 0 + && Math.abs(diffWidth) > 1 && Math.abs(diffHeight) > 1) { + Slog.d(TAG, "Skip wallpaper offset with inconsistent orientation, bounds=" + + lastWallpaperBounds + " frame=" + wallpaperFrame); + // With FLAG_SCALED, the requested size should at least make the frame match one of + // side. If both sides contain differences, the client side may not have updated the + // latest size according to the current orientation. So skip calculating the offset to + // avoid the wallpaper not filling the screen. + return false; + } + int newXOffset = 0; int newYOffset = 0; boolean rawChanged = false; @@ -417,7 +430,7 @@ class WallpaperController { float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; // Difference between width of wallpaper image, and the last size of the wallpaper. // This is the horizontal surplus from the prior configuration. - int availw = wallpaperFrame.width() - lastWallpaperBounds.width(); + int availw = diffWidth; int displayOffset = getDisplayWidthOffset(availw, lastWallpaperBounds, wallpaperWin.isRtl()); @@ -442,9 +455,7 @@ class WallpaperController { float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f; float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f; - int availh = wallpaperWin.getFrame().bottom - wallpaperWin.getFrame().top - - lastWallpaperBounds.height(); - offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0; + offset = diffHeight > 0 ? -(int) (diffHeight * wpy + .5f) : 0; if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) { offset += mLastWallpaperDisplayOffsetY; } diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 92d4cec80cc9..9e7df004806b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -370,6 +370,13 @@ public abstract class WindowManagerInternal { public abstract void requestTraversalFromDisplayManager(); /** + * Called just before display manager has applied the device state to the displays + * @param deviceState device state as defined by + * {@link android.hardware.devicestate.DeviceStateManager} + */ + public abstract void onDisplayManagerReceivedDeviceState(int deviceState); + + /** * Set by the accessibility layer to observe changes in the magnified region, * rotation, and other window transformations related to display magnification * as the window manager is responsible for doing the actual magnification diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e33c6f03c720..67572bfcad18 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -88,6 +88,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_NONE; +import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.fixScale; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW; @@ -3591,6 +3592,7 @@ public class WindowManagerService extends IWindowManager.Stub public void setCurrentUser(@UserIdInt int newUserId) { synchronized (mGlobalLock) { + mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_OPEN, null); mCurrentUserId = newUserId; mPolicy.setCurrentUserLw(newUserId); mKeyguardDisableHandler.setCurrentUser(newUserId); @@ -7674,6 +7676,15 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void onDisplayManagerReceivedDeviceState(int deviceState) { + mH.post(() -> { + synchronized (mGlobalLock) { + mRoot.onDisplayManagerReceivedDeviceState(deviceState); + } + }); + } + + @Override public void setMagnificationSpec(int displayId, MagnificationSpec spec) { synchronized (mGlobalLock) { if (mAccessibilityController.hasCallbacks()) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 3672820c13ad..d7d2b4e9dde2 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -563,7 +563,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } - final long diff = lastLaunchTime - launchTime; + final long diff = launchTime - lastLaunchTime; if (diff < RAPID_ACTIVITY_LAUNCH_MS) { mRapidActivityLaunchCount++; } else if (diff >= RESET_RAPID_ACTIVITY_LAUNCH_MS) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7cd7f6975bbd..8f49384f6101 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3883,13 +3883,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } /** - * @return true if activity bounds are letterboxed or letterboxed for diplay cutout. + * @return {@code true} if activity bounds are letterboxed or letterboxed for display cutout. + * Note that it's always {@code false} if the activity is in pip mode. * * <p>Note that letterbox UI may not be shown even when this returns {@code true}. See {@link * LetterboxUiController#shouldShowLetterboxUi} for more context. */ boolean areAppWindowBoundsLetterboxed() { return mActivityRecord != null + && !mActivityRecord.inPinnedWindowingMode() && (mActivityRecord.areBoundsLetterboxed() || isLetterboxedForDisplayCutout()); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 5ba22830eec9..9c1d765fe0f9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -16,6 +16,7 @@ package com.android.server.devicepolicy; +import static android.app.admin.DevicePolicyIdentifiers.USER_CONTROL_DISABLED_PACKAGES_POLICY; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_TARGET_USER_ID; import static android.app.admin.PolicyUpdateReceiver.EXTRA_POLICY_UPDATE_RESULT_KEY; import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_CONFLICTING_ADMIN_POLICY; @@ -100,7 +101,7 @@ final class DevicePolicyEngine { private final UserManager mUserManager; // TODO(b/256849338): add more granular locks - private final Object mLock = new Object(); + private final Object mLock; /** * Map of <userId, Map<policyKey, policyState>> @@ -121,9 +122,11 @@ final class DevicePolicyEngine { DevicePolicyEngine( @NonNull Context context, - @NonNull DeviceAdminServiceController deviceAdminServiceController) { + @NonNull DeviceAdminServiceController deviceAdminServiceController, + @NonNull Object lock) { mContext = Objects.requireNonNull(context); mDeviceAdminServiceController = Objects.requireNonNull(deviceAdminServiceController); + mLock = Objects.requireNonNull(lock); mUserManager = mContext.getSystemService(UserManager.class); mLocalPolicies = new SparseArray<>(); mGlobalPolicies = new HashMap<>(); @@ -151,8 +154,8 @@ final class DevicePolicyEngine { PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); if (policyDefinition.isNonCoexistablePolicy()) { - setNonCoexistableLocalPolicy(policyDefinition, localPolicyState, enforcingAdmin, - value, userId, skipEnforcePolicy); + setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState, + enforcingAdmin, value, userId, skipEnforcePolicy); return; } @@ -172,10 +175,20 @@ final class DevicePolicyEngine { // the data structures. if (!skipEnforcePolicy) { if (policyChanged) { - onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId); + onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } boolean policyEnforced = Objects.equals( localPolicyState.getCurrentResolvedPolicy(), value); + // TODO(b/285532044): remove hack and handle properly + if (!policyEnforced + && policyDefinition.getPolicyKey().getIdentifier().equals( + USER_CONTROL_DISABLED_PACKAGES_POLICY)) { + PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value; + PolicyValue<Set<String>> parsedResolvedValue = + (PolicyValue<Set<String>>) localPolicyState.getCurrentResolvedPolicy(); + policyEnforced = (parsedResolvedValue != null && parsedValue != null + && parsedResolvedValue.getValue().containsAll(parsedValue.getValue())); + } sendPolicyResultToAdmin( enforcingAdmin, policyDefinition, @@ -200,7 +213,7 @@ final class DevicePolicyEngine { * * <p>Passing a {@code null} value means the policy set by this admin should be removed. */ - private <V> void setNonCoexistableLocalPolicy( + private <V> void setNonCoexistableLocalPolicyLocked( PolicyDefinition<V> policyDefinition, PolicyState<V> localPolicyState, EnforcingAdmin enforcingAdmin, @@ -255,8 +268,8 @@ final class DevicePolicyEngine { PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId); if (policyDefinition.isNonCoexistablePolicy()) { - setNonCoexistableLocalPolicy(policyDefinition, localPolicyState, enforcingAdmin, - /* value= */ null, userId, /* skipEnforcePolicy= */ false); + setNonCoexistableLocalPolicyLocked(policyDefinition, localPolicyState, + enforcingAdmin, /* value= */ null, userId, /* skipEnforcePolicy= */ false); return; } @@ -271,7 +284,7 @@ final class DevicePolicyEngine { } if (policyChanged) { - onLocalPolicyChanged(policyDefinition, enforcingAdmin, userId); + onLocalPolicyChangedLocked(policyDefinition, enforcingAdmin, userId); } // For a removePolicy to be enforced, it means no current policy exists @@ -337,7 +350,7 @@ final class DevicePolicyEngine { /** * Enforces the new policy and notifies relevant admins. */ - private <V> void onLocalPolicyChanged( + private <V> void onLocalPolicyChangedLocked( @NonNull PolicyDefinition<V> policyDefinition, @NonNull EnforcingAdmin enforcingAdmin, int userId) { @@ -347,7 +360,7 @@ final class DevicePolicyEngine { policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId); // Send policy updates to admins who've set it locally - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( localPolicyState, enforcingAdmin, policyDefinition, @@ -358,7 +371,7 @@ final class DevicePolicyEngine { // Send policy updates to admins who've set it globally if (hasGlobalPolicyLocked(policyDefinition)) { PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition); - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( globalPolicyState, enforcingAdmin, policyDefinition, @@ -413,11 +426,22 @@ final class DevicePolicyEngine { // the data structures. if (!skipEnforcePolicy) { if (policyChanged) { - onGlobalPolicyChanged(policyDefinition, enforcingAdmin); + onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } boolean policyAppliedGlobally = Objects.equals( globalPolicyState.getCurrentResolvedPolicy(), value); + // TODO(b/285532044): remove hack and handle properly + if (!policyAppliedGlobally + && policyDefinition.getPolicyKey().getIdentifier().equals( + USER_CONTROL_DISABLED_PACKAGES_POLICY)) { + PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value; + PolicyValue<Set<String>> parsedResolvedValue = + (PolicyValue<Set<String>>) globalPolicyState.getCurrentResolvedPolicy(); + policyAppliedGlobally = (parsedResolvedValue != null && parsedValue != null + && parsedResolvedValue.getValue().containsAll(parsedValue.getValue())); + } + boolean policyApplied = policyAppliedGlobally && policyAppliedOnAllUsers; sendPolicyResultToAdmin( @@ -451,7 +475,7 @@ final class DevicePolicyEngine { boolean policyChanged = policyState.removePolicy(enforcingAdmin); if (policyChanged) { - onGlobalPolicyChanged(policyDefinition, enforcingAdmin); + onGlobalPolicyChangedLocked(policyDefinition, enforcingAdmin); } applyGlobalPolicyOnUsersWithLocalPoliciesLocked(policyDefinition, enforcingAdmin, @@ -477,7 +501,7 @@ final class DevicePolicyEngine { /** * Enforces the new policy globally and notifies relevant admins. */ - private <V> void onGlobalPolicyChanged( + private <V> void onGlobalPolicyChangedLocked( @NonNull PolicyDefinition<V> policyDefinition, @NonNull EnforcingAdmin enforcingAdmin) { PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition); @@ -485,7 +509,7 @@ final class DevicePolicyEngine { enforcePolicy(policyDefinition, policyState.getCurrentResolvedPolicy(), UserHandle.USER_ALL); - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( policyState, enforcingAdmin, policyDefinition, @@ -530,7 +554,7 @@ final class DevicePolicyEngine { policyDefinition, localPolicyState.getCurrentResolvedPolicy(), userId); - sendPolicyChangedToAdmins( + sendPolicyChangedToAdminsLocked( localPolicyState, enforcingAdmin, policyDefinition, @@ -539,8 +563,20 @@ final class DevicePolicyEngine { userId); } - isAdminPolicyApplied &= Objects.equals( - value, localPolicyState.getCurrentResolvedPolicy()); + // TODO(b/285532044): remove hack and handle properly + if (policyDefinition.getPolicyKey().getIdentifier().equals( + USER_CONTROL_DISABLED_PACKAGES_POLICY)) { + if (!Objects.equals(value, localPolicyState.getCurrentResolvedPolicy())) { + PolicyValue<Set<String>> parsedValue = (PolicyValue<Set<String>>) value; + PolicyValue<Set<String>> parsedResolvedValue = + (PolicyValue<Set<String>>) localPolicyState.getCurrentResolvedPolicy(); + isAdminPolicyApplied &= (parsedResolvedValue != null && parsedValue != null + && parsedResolvedValue.getValue().containsAll(parsedValue.getValue())); + } + } else { + isAdminPolicyApplied &= Objects.equals( + value, localPolicyState.getCurrentResolvedPolicy()); + } } return isAdminPolicyApplied; } @@ -711,34 +747,35 @@ final class DevicePolicyEngine { } <V> void transferPolicies(EnforcingAdmin oldAdmin, EnforcingAdmin newAdmin) { - Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); - for (PolicyKey policy : globalPolicies) { - PolicyState<?> policyState = mGlobalPolicies.get(policy); - if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { - PolicyDefinition<V> policyDefinition = - (PolicyDefinition<V>) policyState.getPolicyDefinition(); - PolicyValue<V> policyValue = - (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); - setGlobalPolicy(policyDefinition, newAdmin, policyValue); - } - } - - for (int i = 0; i < mLocalPolicies.size(); i++) { - int userId = mLocalPolicies.keyAt(i); - Set<PolicyKey> localPolicies = new HashSet<>( - mLocalPolicies.get(userId).keySet()); - for (PolicyKey policy : localPolicies) { - PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + synchronized (mLock) { + Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); + for (PolicyKey policy : globalPolicies) { + PolicyState<?> policyState = mGlobalPolicies.get(policy); if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { PolicyDefinition<V> policyDefinition = (PolicyDefinition<V>) policyState.getPolicyDefinition(); PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); - setLocalPolicy(policyDefinition, newAdmin, policyValue, userId); + setGlobalPolicy(policyDefinition, newAdmin, policyValue); } } - } + for (int i = 0; i < mLocalPolicies.size(); i++) { + int userId = mLocalPolicies.keyAt(i); + Set<PolicyKey> localPolicies = new HashSet<>( + mLocalPolicies.get(userId).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(oldAdmin)) { + PolicyDefinition<V> policyDefinition = + (PolicyDefinition<V>) policyState.getPolicyDefinition(); + PolicyValue<V> policyValue = + (PolicyValue<V>) policyState.getPoliciesSetByAdmins().get(oldAdmin); + setLocalPolicy(policyDefinition, newAdmin, policyValue, userId); + } + } + } + } removePoliciesForAdmin(oldAdmin); } @@ -802,7 +839,7 @@ final class DevicePolicyEngine { mLocalPolicies.get(userId).put( policyDefinition.getPolicyKey(), new PolicyState<>(policyDefinition)); } - return getPolicyState(mLocalPolicies.get(userId), policyDefinition); + return getPolicyStateLocked(mLocalPolicies.get(userId), policyDefinition); } private <V> void removeLocalPolicyStateLocked( @@ -824,14 +861,14 @@ final class DevicePolicyEngine { mGlobalPolicies.put( policyDefinition.getPolicyKey(), new PolicyState<>(policyDefinition)); } - return getPolicyState(mGlobalPolicies, policyDefinition); + return getPolicyStateLocked(mGlobalPolicies, policyDefinition); } private <V> void removeGlobalPolicyStateLocked(PolicyDefinition<V> policyDefinition) { mGlobalPolicies.remove(policyDefinition.getPolicyKey()); } - private static <V> PolicyState<V> getPolicyState( + private static <V> PolicyState<V> getPolicyStateLocked( Map<PolicyKey, PolicyState<?>> policies, PolicyDefinition<V> policyDefinition) { try { // This will not throw an exception because policyDefinition is of type V, so unless @@ -901,7 +938,7 @@ final class DevicePolicyEngine { } // TODO(b/261430877): Finalise the decision on which admins to send the updates to. - private <V> void sendPolicyChangedToAdmins( + private <V> void sendPolicyChangedToAdminsLocked( PolicyState<V> policyState, EnforcingAdmin callingAdmin, PolicyDefinition<V> policyDefinition, @@ -1176,17 +1213,19 @@ final class DevicePolicyEngine { if (parentInfo == null || parentInfo.getUserHandle().getIdentifier() == userId) { return; } - if (!mLocalPolicies.contains(parentInfo.getUserHandle().getIdentifier())) { - return; - } - for (Map.Entry<PolicyKey, PolicyState<?>> entry : mLocalPolicies.get( - parentInfo.getUserHandle().getIdentifier()).entrySet()) { - enforcePolicyOnUser(userId, entry.getValue()); + synchronized (mLock) { + if (!mLocalPolicies.contains(parentInfo.getUserHandle().getIdentifier())) { + return; + } + for (Map.Entry<PolicyKey, PolicyState<?>> entry : mLocalPolicies.get( + parentInfo.getUserHandle().getIdentifier()).entrySet()) { + enforcePolicyOnUserLocked(userId, entry.getValue()); + } } }); } - private <V> void enforcePolicyOnUser(int userId, PolicyState<V> policyState) { + private <V> void enforcePolicyOnUserLocked(int userId, PolicyState<V> policyState) { if (!policyState.getPolicyDefinition().isInheritable()) { return; } @@ -1205,26 +1244,28 @@ final class DevicePolicyEngine { */ @NonNull DevicePolicyState getDevicePolicyState() { - Map<UserHandle, Map<PolicyKey, android.app.admin.PolicyState<?>>> policies = - new HashMap<>(); - for (int i = 0; i < mLocalPolicies.size(); i++) { - UserHandle user = UserHandle.of(mLocalPolicies.keyAt(i)); - policies.put(user, new HashMap<>()); - for (PolicyKey policyKey : mLocalPolicies.valueAt(i).keySet()) { - policies.get(user).put( - policyKey, - mLocalPolicies.valueAt(i).get(policyKey).getParcelablePolicyState()); + synchronized (mLock) { + Map<UserHandle, Map<PolicyKey, android.app.admin.PolicyState<?>>> policies = + new HashMap<>(); + for (int i = 0; i < mLocalPolicies.size(); i++) { + UserHandle user = UserHandle.of(mLocalPolicies.keyAt(i)); + policies.put(user, new HashMap<>()); + for (PolicyKey policyKey : mLocalPolicies.valueAt(i).keySet()) { + policies.get(user).put( + policyKey, + mLocalPolicies.valueAt(i).get(policyKey).getParcelablePolicyState()); + } } - } - if (!mGlobalPolicies.isEmpty()) { - policies.put(UserHandle.ALL, new HashMap<>()); - for (PolicyKey policyKey : mGlobalPolicies.keySet()) { - policies.get(UserHandle.ALL).put( - policyKey, - mGlobalPolicies.get(policyKey).getParcelablePolicyState()); + if (!mGlobalPolicies.isEmpty()) { + policies.put(UserHandle.ALL, new HashMap<>()); + for (PolicyKey policyKey : mGlobalPolicies.keySet()) { + policies.get(UserHandle.ALL).put( + policyKey, + mGlobalPolicies.get(policyKey).getParcelablePolicyState()); + } } + return new DevicePolicyState(policies); } - return new DevicePolicyState(policies); } @@ -1232,23 +1273,25 @@ final class DevicePolicyEngine { * Removes all local and global policies set by that admin. */ void removePoliciesForAdmin(EnforcingAdmin admin) { - Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); - for (PolicyKey policy : globalPolicies) { - PolicyState<?> policyState = mGlobalPolicies.get(policy); - if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { - removeGlobalPolicy(policyState.getPolicyDefinition(), admin); + synchronized (mLock) { + Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet()); + for (PolicyKey policy : globalPolicies) { + PolicyState<?> policyState = mGlobalPolicies.get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { + removeGlobalPolicy(policyState.getPolicyDefinition(), admin); + } } - } - for (int i = 0; i < mLocalPolicies.size(); i++) { - Set<PolicyKey> localPolicies = new HashSet<>( - mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet()); - for (PolicyKey policy : localPolicies) { - PolicyState<?> policyState = mLocalPolicies.get( - mLocalPolicies.keyAt(i)).get(policy); - if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { - removeLocalPolicy( - policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i)); + for (int i = 0; i < mLocalPolicies.size(); i++) { + Set<PolicyKey> localPolicies = new HashSet<>( + mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get( + mLocalPolicies.keyAt(i)).get(policy); + if (policyState.getPoliciesSetByAdmins().containsKey(admin)) { + removeLocalPolicy( + policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i)); + } } } } @@ -1258,23 +1301,25 @@ final class DevicePolicyEngine { * Removes all local policies for the provided {@code userId}. */ private void removeLocalPoliciesForUser(int userId) { - if (!mLocalPolicies.contains(userId)) { - // No policies on user - return; - } + synchronized (mLock) { + if (!mLocalPolicies.contains(userId)) { + // No policies on user + return; + } - Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet()); - for (PolicyKey policy : localPolicies) { - PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); - Set<EnforcingAdmin> admins = new HashSet<>( - policyState.getPoliciesSetByAdmins().keySet()); - for (EnforcingAdmin admin : admins) { - removeLocalPolicy( - policyState.getPolicyDefinition(), admin, userId); + Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet()); + for (PolicyKey policy : localPolicies) { + PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy); + Set<EnforcingAdmin> admins = new HashSet<>( + policyState.getPoliciesSetByAdmins().keySet()); + for (EnforcingAdmin admin : admins) { + removeLocalPolicy( + policyState.getPolicyDefinition(), admin, userId); + } } - } - mLocalPolicies.remove(userId); + mLocalPolicies.remove(userId); + } } /** @@ -1342,7 +1387,7 @@ final class DevicePolicyEngine { */ private void updateDeviceAdminServiceOnPolicyRemoveLocked( @NonNull EnforcingAdmin enforcingAdmin) { - if (doesAdminHavePolicies(enforcingAdmin)) { + if (doesAdminHavePoliciesLocked(enforcingAdmin)) { return; } int userId = enforcingAdmin.getUserId(); @@ -1365,7 +1410,7 @@ final class DevicePolicyEngine { /* actionForLog= */ "policy-removed"); } - private boolean doesAdminHavePolicies(@NonNull EnforcingAdmin enforcingAdmin) { + private boolean doesAdminHavePoliciesLocked(@NonNull EnforcingAdmin enforcingAdmin) { for (PolicyKey policy : mGlobalPolicies.keySet()) { PolicyState<?> policyState = mGlobalPolicies.get(policy); if (policyState.getPoliciesSetByAdmins().containsKey(enforcingAdmin)) { @@ -1386,13 +1431,17 @@ final class DevicePolicyEngine { @NonNull private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) { - return mEnforcingAdmins.contains(userId) - ? mEnforcingAdmins.get(userId) : Collections.emptySet(); + synchronized (mLock) { + return mEnforcingAdmins.contains(userId) + ? mEnforcingAdmins.get(userId) : Collections.emptySet(); + } } private void write() { - Log.d(TAG, "Writing device policies to file."); - new DevicePoliciesReaderWriter().writeToFileLocked(); + synchronized (mLock) { + Log.d(TAG, "Writing device policies to file."); + new DevicePoliciesReaderWriter().writeToFileLocked(); + } } // TODO(b/256852787): trigger resolving logic after loading policies as roles are recalculated @@ -1402,11 +1451,11 @@ final class DevicePolicyEngine { synchronized (mLock) { clear(); new DevicePoliciesReaderWriter().readFromFileLocked(); - reapplyAllPolicies(); + reapplyAllPoliciesLocked(); } } - private <V> void reapplyAllPolicies() { + private <V> void reapplyAllPoliciesLocked() { for (PolicyKey policy : mGlobalPolicies.keySet()) { PolicyState<?> policyState = mGlobalPolicies.get(policy); // Policy definition and value will always be of the same type @@ -1436,10 +1485,8 @@ final class DevicePolicyEngine { * <p>Note that this doesn't clear any enforcements, it only clears the data structures. */ void clearAllPolicies() { - synchronized (mLock) { - clear(); - write(); - } + clear(); + write(); } private void clear() { synchronized (mLock) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index edb8e0c5aa64..e44b8cdbaab0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -49,6 +49,7 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_LOCK_TASK; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MICROPHONE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK; +import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_MTE; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY; @@ -73,7 +74,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER; -import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USERS; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_VPN; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WALLPAPER; import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI; @@ -139,6 +139,7 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATI import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD; @@ -2092,7 +2093,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mUserData = new SparseArray<>(); mOwners = makeOwners(injector, pathProvider); - mDevicePolicyEngine = new DevicePolicyEngine(mContext, mDeviceAdminServiceController); + mDevicePolicyEngine = new DevicePolicyEngine( + mContext, mDeviceAdminServiceController, getLockObject()); if (!mHasFeature) { // Skip the rest of the initialization @@ -7839,27 +7841,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { throw new SecurityException("Cannot wipe data. " + restriction + " restriction is set for user " + userId); } + }); - boolean isSystemUser = userId == UserHandle.USER_SYSTEM; - boolean wipeDevice; - if (factoryReset == null || !mInjector.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR, - adminPackage, - userId)) { - // Legacy mode - wipeDevice = isSystemUser; + boolean isSystemUser = userId == UserHandle.USER_SYSTEM; + boolean wipeDevice; + if (factoryReset == null || !mInjector.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR, + adminPackage, + userId)) { + // Legacy mode + wipeDevice = isSystemUser; + } else { + // Explicit behaviour + if (factoryReset) { + EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( + /*admin=*/ null, + /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, + MASTER_CLEAR}, + USES_POLICY_WIPE_DATA, + adminPackage, + factoryReset ? UserHandle.USER_ALL : + getAffectedUser(calledOnParentInstance)); + wipeDevice = true; } else { - // Explicit behaviour - if (factoryReset) { - EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin( - /*admin=*/ null, - /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA, - MASTER_CLEAR}, - USES_POLICY_WIPE_DATA, - adminPackage, - factoryReset ? UserHandle.USER_ALL : - getAffectedUser(calledOnParentInstance)); - wipeDevice = true; - } else { + mInjector.binderWithCleanCallingIdentity(() -> { Preconditions.checkCallAuthorization(!isSystemUser, "User %s is a system user and cannot be removed", userId); boolean isLastNonHeadlessUser = getUserInfo(userId).isFull() @@ -7870,9 +7874,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Removing user %s would leave the device without any active users. " + "Consider factory resetting the device instead.", userId); - wipeDevice = false; - } + }); + wipeDevice = false; } + } + mInjector.binderWithCleanCallingIdentity(() -> { if (wipeDevice) { forceWipeDeviceNoLock( (flags & WIPE_EXTERNAL_STORAGE) != 0, @@ -13720,7 +13726,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_ADD_CLONE_PROFILE, new String[]{MANAGE_DEVICE_POLICY_PROFILES}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_ADD_USER, new String[]{MANAGE_DEVICE_POLICY_USERS}); + UserManager.DISALLOW_ADD_USER, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_ADD_WIFI_CONFIG, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( @@ -13810,13 +13816,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_PRINTING, new String[]{MANAGE_DEVICE_POLICY_PRINTING}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_REMOVE_USER, new String[]{MANAGE_DEVICE_POLICY_USERS}); + UserManager.DISALLOW_REMOVE_USER, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_RUN_IN_BACKGROUND, new String[]{MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_SAFE_BOOT, new String[]{MANAGE_DEVICE_POLICY_SAFE_BOOT}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_SET_USER_ICON, new String[]{MANAGE_DEVICE_POLICY_USERS}); + UserManager.DISALLOW_SET_USER_ICON, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_SET_WALLPAPER, new String[]{MANAGE_DEVICE_POLICY_WALLPAPER}); USER_RESTRICTION_PERMISSIONS.put( @@ -13842,7 +13848,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_USB_FILE_TRANSFER, new String[]{MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER}); USER_RESTRICTION_PERMISSIONS.put( - UserManager.DISALLOW_USER_SWITCH, new String[]{MANAGE_DEVICE_POLICY_USERS}); + UserManager.DISALLOW_USER_SWITCH, new String[]{MANAGE_DEVICE_POLICY_MODIFY_USERS}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_WIFI_DIRECT, new String[]{MANAGE_DEVICE_POLICY_WIFI}); USER_RESTRICTION_PERMISSIONS.put( @@ -15139,7 +15145,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void enforceCanSetLockTaskFeaturesOnFinancedDevice(CallerIdentity caller, int flags) { int allowedFlags = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS - | LOCK_TASK_FEATURE_NOTIFICATIONS; + | LOCK_TASK_FEATURE_NOTIFICATIONS | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK; if (!isFinancedDeviceOwner(caller)) { return; @@ -15150,7 +15156,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Permitted lock task features when managing a financed device: " + "LOCK_TASK_FEATURE_SYSTEM_INFO, LOCK_TASK_FEATURE_KEYGUARD, " + "LOCK_TASK_FEATURE_HOME, LOCK_TASK_FEATURE_GLOBAL_ACTIONS, " - + "or LOCK_TASK_FEATURE_NOTIFICATIONS"); + + "LOCK_TASK_FEATURE_NOTIFICATIONS" + + " or LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK"); } } @@ -23019,6 +23026,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_LOCK_TASK, MANAGE_DEVICE_POLICY_MICROPHONE, MANAGE_DEVICE_POLICY_MOBILE_NETWORK, + MANAGE_DEVICE_POLICY_MODIFY_USERS, MANAGE_DEVICE_POLICY_MTE, MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION, MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, @@ -23042,7 +23050,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_TIME, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, - MANAGE_DEVICE_POLICY_USERS, MANAGE_DEVICE_POLICY_VPN, MANAGE_DEVICE_POLICY_WALLPAPER, MANAGE_DEVICE_POLICY_WIFI, @@ -23063,12 +23070,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_KEYGUARD, MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS, MANAGE_DEVICE_POLICY_LOCK_TASK, + MANAGE_DEVICE_POLICY_MODIFY_USERS, MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS, MANAGE_DEVICE_POLICY_SAFE_BOOT, MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE, MANAGE_DEVICE_POLICY_TIME, - MANAGE_DEVICE_POLICY_USERS, MANAGE_DEVICE_POLICY_WIPE_DATA ); @@ -23154,6 +23161,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_FUN, MANAGE_DEVICE_POLICY_LOCK_TASK, MANAGE_DEVICE_POLICY_MOBILE_NETWORK, + MANAGE_DEVICE_POLICY_MODIFY_USERS, MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA, MANAGE_DEVICE_POLICY_PRINTING, MANAGE_DEVICE_POLICY_PROFILES, @@ -23162,7 +23170,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_SMS, MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, - MANAGE_DEVICE_POLICY_USERS, MANAGE_DEVICE_POLICY_WINDOWS, SET_TIME, SET_TIME_ZONE @@ -23352,6 +23359,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_LOCK_TASK, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); + CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MODIFY_USERS, + MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY, @@ -23372,8 +23381,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); - CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_USERS, - MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_VPN, MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL); CROSS_USER_PERMISSIONS.put(MANAGE_DEVICE_POLICY_WALLPAPER, diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index 774f62d44045..fd478dc12c13 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -31,6 +31,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -245,10 +246,13 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks, intent.putExtra(PrintManager.EXTRA_PRINT_JOB, printJob); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + ActivityOptions activityOptions = ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED); IntentSender intentSender = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT - | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, - null, new UserHandle(mUserId)) .getIntentSender(); + | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + activityOptions.toBundle(), new UserHandle(mUserId)).getIntentSender(); Bundle result = new Bundle(); result.putParcelable(PrintManager.EXTRA_PRINT_JOB, printJob); diff --git a/services/tests/PackageManagerServiceTests/server/Android.bp b/services/tests/PackageManagerServiceTests/server/Android.bp index 1146271368af..3176f0673b65 100644 --- a/services/tests/PackageManagerServiceTests/server/Android.bp +++ b/services/tests/PackageManagerServiceTests/server/Android.bp @@ -106,6 +106,10 @@ android_test { ":PackageParserTestApp6", ], resource_zips: [":PackageManagerServiceServerTests_apks_as_resources"], + + data: [ + ":StubTestApp", + ], } // Rules to copy all the test apks to the intermediate raw resource directory diff --git a/services/tests/PackageManagerServiceTests/server/AndroidTest.xml b/services/tests/PackageManagerServiceTests/server/AndroidTest.xml index 1b93527065ec..a0ef03c69e3a 100644 --- a/services/tests/PackageManagerServiceTests/server/AndroidTest.xml +++ b/services/tests/PackageManagerServiceTests/server/AndroidTest.xml @@ -23,6 +23,14 @@ <option name="install-arg" value="-g" /> <option name="test-file-name" value="PackageManagerServiceServerTests.apk" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="settings put global verifier_engprod 1" /> + </target_preparer> + + <!-- Load additional APKs onto device --> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/> + </target_preparer> <option name="test-tag" value="PackageManagerServiceServerTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 b/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 Binary files differindex 138b6113ea6b..3683dca64449 100644 --- a/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 +++ b/services/tests/PackageManagerServiceTests/server/res/raw/install_app1_cert5 diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java index b82ffb4d0b39..435f0d7f5f15 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java @@ -32,7 +32,6 @@ import android.app.AppGlobals; import android.content.IIntentReceiver; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; @@ -70,7 +69,7 @@ import java.util.regex.Pattern; @RunWith(AndroidJUnit4.class) public class PackageManagerServiceTest { - private static final String PACKAGE_NAME = "com.android.frameworks.servicestests"; + private static final String PACKAGE_NAME = "com.android.server.pm.test.service.server"; private static final String TEST_DATA_PATH = "/data/local/tmp/servicestests/"; private static final String TEST_APP_APK = "StubTestApp.apk"; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index a614c4dd1d61..2bc66ace454b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -27,6 +27,7 @@ import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.util.DebugUtils.valueToString; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -742,24 +743,24 @@ public class ActivityManagerServiceTest { broadcastIntent(intent1, null, true); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), - StickyBroadcast.create(intent1, false, Process.myUid())); + StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertNull(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER)); assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER)); broadcastIntent(intent2, options.toBundle(), true); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), - StickyBroadcast.create(intent1, false, Process.myUid())); + StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER), - StickyBroadcast.create(intent2, true, Process.myUid())); + StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertNull(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER)); broadcastIntent(intent3, null, true); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION1, TEST_USER), - StickyBroadcast.create(intent1, false, Process.myUid())); + StickyBroadcast.create(intent1, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION2, TEST_USER), - StickyBroadcast.create(intent2, true, Process.myUid())); + StickyBroadcast.create(intent2, true, Process.myUid(), PROCESS_STATE_UNKNOWN)); assertStickyBroadcasts(mAms.getStickyBroadcasts(TEST_ACTION3, TEST_USER), - StickyBroadcast.create(intent3, false, Process.myUid())); + StickyBroadcast.create(intent3, false, Process.myUid(), PROCESS_STATE_UNKNOWN)); } @SuppressWarnings("GuardedBy") diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 6bce71e8c9b0..f4238f678f08 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -16,6 +16,8 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; @@ -246,7 +248,7 @@ public final class BroadcastQueueModernImplTest { return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo, Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM, - BackgroundStartPrivileges.NONE, false, null); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); } private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, @@ -1092,6 +1094,17 @@ public final class BroadcastQueueModernImplTest { verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); verifyPendingRecords(redQueue, List.of(screenOff)); verifyPendingRecords(blueQueue, List.of(screenOff, screenOn)); + + final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), resultTo, false); + screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, + "testDeliveryGroupPolicy_resultTo_diffReceivers"); + mImpl.enqueueBroadcastLocked(screenOffRecord); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), resultTo, false)); + verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); } @Test @@ -1277,6 +1290,36 @@ public final class BroadcastQueueModernImplTest { } @Test + public void testDeliveryGroupPolicy_merged_multipleReceivers() { + final long now = SystemClock.elapsedRealtime(); + final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast1 = createDropboxBroadcast( + "TAG_A", now, 2); + final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast2 = createDropboxBroadcast( + "TAG_A", now + 1000, 4); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first, + dropboxEntryBroadcast1.second, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_RED, CLASS_RED)), + false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first, + dropboxEntryBroadcast2.second, + List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), + makeManifestReceiver(PACKAGE_RED, CLASS_RED)), + false)); + + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + + verifyPendingRecords(greenQueue, + List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); + verifyPendingRecords(redQueue, + List.of(dropboxEntryBroadcast1.first, dropboxEntryBroadcast2.first)); + } + + @Test public void testDeliveryGroupPolicy_sameAction_differentMatchingCriteria() { final Intent closeSystemDialogs1 = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); final BroadcastOptions optionsCloseSystemDialog1 = BroadcastOptions.makeBasic() @@ -1406,7 +1449,8 @@ public final class BroadcastQueueModernImplTest { eq(getUidForPackage(PACKAGE_GREEN)), anyInt(), eq(Intent.ACTION_TIME_TICK), eq(BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST), eq(BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD), - anyLong(), anyLong(), anyLong(), anyInt(), nullable(String.class), anyString()), + anyLong(), anyLong(), anyLong(), anyInt(), nullable(String.class), + anyString(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt()), times(1)); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index 4989f841a275..0f75ea5fb7ef 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER; import static android.os.UserHandle.USER_SYSTEM; @@ -611,7 +612,7 @@ public class BroadcastQueueTest { callerApp.getPid(), callerApp.info.uid, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo, Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId, - BackgroundStartPrivileges.NONE, false, null); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); } private void assertHealth() { @@ -1599,7 +1600,7 @@ public class BroadcastQueueTest { null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(), List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null, Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM, - backgroundStartPrivileges, false, null); + backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN); enqueueBroadcast(r); waitForIdle(); @@ -1904,6 +1905,34 @@ public class BroadcastQueueTest { } @Test + public void testReplacePending_diffReceivers() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp); + final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp); + final BroadcastFilter receiverYellow = makeRegisteredReceiver(receiverYellowApp); + + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED) + .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of( + withPriority(receiverGreen, 10), + withPriority(receiverBlue, 5), + withPriority(receiverYellow, 0)))); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, List.of( + withPriority(receiverGreen, 10), + withPriority(receiverBlue, 5)))); + + waitForIdle(); + + verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane); + verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, airplane); + verifyScheduleRegisteredReceiver(never(), receiverYellowApp, airplane); + } + + @Test public void testIdleAndBarrier() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java index 08952eab071f..f0efb795f5a1 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.ACTION_LOCKED_BOOT_COMPLETED; @@ -958,7 +959,8 @@ public class BroadcastRecordTest { userId, BackgroundStartPrivileges.NONE, false /* timeoutExempt */, - filterExtrasForReceiver); + filterExtrasForReceiver, + PROCESS_STATE_UNKNOWN); } private static int getAppId(int i) { diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java index c5ff8cc7b0d6..dd23d9f006e3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java @@ -28,6 +28,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; +import static com.android.server.job.JobSchedulerService.sUptimeMillisClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -148,6 +149,9 @@ public class JobSchedulerServiceTest { // Used in JobConcurrencyManager. doReturn(mock(UserManagerInternal.class)) .when(() -> LocalServices.getService(UserManagerInternal.class)); + // Used in JobStatus. + doReturn(mock(JobSchedulerInternal.class)) + .when(() -> LocalServices.getService(JobSchedulerInternal.class)); // Called via IdleController constructor. when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); when(mContext.getResources()).thenReturn(mock(Resources.class)); @@ -168,6 +172,8 @@ public class JobSchedulerServiceTest { JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + // Make sure the uptime is at least 24 hours so that tests that rely on high uptime work. + sUptimeMillisClock = getAdvancedClock(sUptimeMillisClock, 24 * HOUR_IN_MILLIS); // Called by DeviceIdlenessTracker when(mContext.getSystemService(UiModeManager.class)).thenReturn(mock(UiModeManager.class)); @@ -313,6 +319,260 @@ public class JobSchedulerServiceTest { } @Test + public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_disabled() { + JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // Safeguards disabled -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 UIJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 UIJ timeouts. Safeguards disabled -> no penalties. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 EJ timeouts. Safeguards disabled -> no penalties. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 Reg timeouts. Safeguards disabled -> no penalties. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + } + + @Test + public void testGetMinJobExecutionGuaranteeMs_timeoutSafeguards_enabled() { + JobStatus jobUij = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMinJobExecutionGuaranteeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // No timeouts -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 UIJ timeout. No execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // Not a timeout -> 1 UIJ timeout. No execution penalty yet. + jobUij.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 UIJ timeouts. Min execution penalty only for UIJs. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 EJ timeouts. Max execution penalty for EJs. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + + // 2 Reg timeouts. Max execution penalty for regular jobs. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMinJobExecutionGuaranteeMs(jobReg)); + } + + @Test public void testGetMaxJobExecutionTimeMs() { JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs", createJobInfo(10) @@ -327,7 +587,7 @@ public class JobSchedulerServiceTest { doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) - .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); + .when(tareController).getMaxJobExecutionTimeMsLocked(any()); grantRunUserInitiatedJobsPermission(true); assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, @@ -337,6 +597,306 @@ public class JobSchedulerServiceTest { mService.getMaxJobExecutionTimeMs(jobUIDT)); } + @Test + public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_disabled() { + JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + QuotaController quotaController = mService.getQuotaController(); + spyOn(quotaController); + TareController tareController = mService.getTareController(); + spyOn(tareController); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(tareController).getMaxJobExecutionTimeMsLocked(any()); + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = false; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // Safeguards disabled -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 UIJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 UIJ timeouts. Safeguards disabled -> no penalties. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 EJ timeouts. Safeguards disabled -> no penalties. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 Reg timeouts. Safeguards disabled -> no penalties. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + } + + @Test + public void testGetMaxJobExecutionTimeMs_timeoutSafeguards_enabled() { + JobStatus jobUij = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(1) + .setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); + JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(2).setExpedited(true)); + JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs_timeoutSafeguards", + createJobInfo(3)); + spyOn(jobUij); + when(jobUij.shouldTreatAsUserInitiatedJob()).thenReturn(true); + jobUij.startedAsUserInitiatedJob = true; + spyOn(jobEj); + when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true); + jobEj.startedAsExpeditedJob = true; + + QuotaController quotaController = mService.getQuotaController(); + spyOn(quotaController); + TareController tareController = mService.getTareController(); + spyOn(tareController); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(quotaController).getMaxJobExecutionTimeMsLocked(any()); + doReturn(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) + .when(tareController).getMaxJobExecutionTimeMsLocked(any()); + + mService.mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC = true; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 2; + mService.mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 2; + mService.updateQuotaTracker(); + mService.resetScheduleQuota(); + + // No timeouts -> no penalties. + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 UIJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // Not a timeout -> 1 UIJ timeout. No max execution penalty yet. + jobUij.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_UI_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 UIJ timeouts. Max execution penalty only for UIJs. + jobUij.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_UI_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobUij, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 EJ timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // Not a timeout -> 1 EJ timeout. No max execution penalty yet. + jobEj.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 EJ timeouts. Max execution penalty for EJs. + jobEj.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobEj, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 1 reg timeout. No max execution penalty yet. + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_TIMEOUT); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // Not a timeout -> 1 reg timeout. No max execution penalty yet. + jobReg.madeActive = sUptimeMillisClock.millis() - 1; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + + // 2 Reg timeouts. Max execution penalty for regular jobs. + jobReg.madeActive = + sUptimeMillisClock.millis() - mService.mConstants.RUNTIME_MIN_GUARANTEE_MS; + mService.maybeProcessBuggyJob(jobReg, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); + grantRunUserInitiatedJobsPermission(true); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + grantRunUserInitiatedJobsPermission(false); + assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, + mService.getMaxJobExecutionTimeMs(jobUij)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobEj)); + assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS, + mService.getMaxJobExecutionTimeMs(jobReg)); + } + /** * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} @@ -1226,6 +1786,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1249,6 +1810,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1270,6 +1832,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1292,6 +1855,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = true; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); for (int i = 0; i < 500; ++i) { @@ -1315,6 +1879,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(false).build(); final JobWorkItem item = new JobWorkItem.Builder().build(); @@ -1337,6 +1902,7 @@ public class JobSchedulerServiceTest { mService.mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION = false; mService.mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false; mService.updateQuotaTracker(); + mService.resetScheduleQuota(); final JobInfo job = createJobInfo().setPersisted(true).build(); final JobWorkItem item = new JobWorkItem.Builder().build(); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 2180a781e437..2b56ea8bba33 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -73,6 +73,7 @@ import android.telephony.TelephonyManager; import android.util.DataUnit; import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.net.NetworkPolicyManagerInternal; @@ -124,6 +125,10 @@ public class ConnectivityControllerTest { LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal); + // Used in JobStatus. + LocalServices.removeServiceForTest(JobSchedulerInternal.class); + LocalServices.addService(JobSchedulerInternal.class, mock(JobSchedulerInternal.class)); + // Freeze the clocks at this moment in time JobSchedulerService.sSystemClock = Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 05780ebe6c4b..1de7e3719112 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; +import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; @@ -45,6 +46,8 @@ import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.when; @@ -582,6 +585,40 @@ public class JobStatusTest { } @Test + public void testGetEffectiveStandbyBucket_buggyApp() { + when(mJobSchedulerInternal.isAppConsideredBuggy( + anyInt(), anyString(), anyInt(), anyString())) + .thenReturn(true); + + final JobInfo jobInfo = new JobInfo.Builder(1234, TEST_JOB_COMPONENT).build(); + JobStatus job = createJobStatus(jobInfo); + + // Exempt apps be exempting. + job.setStandbyBucket(EXEMPTED_INDEX); + assertEquals(EXEMPTED_INDEX, job.getEffectiveStandbyBucket()); + + // Actual bucket is higher than the buggy cap, so the cap comes into effect. + job.setStandbyBucket(ACTIVE_INDEX); + assertEquals(WORKING_INDEX, job.getEffectiveStandbyBucket()); + + // Buckets at the cap or below shouldn't be affected. + job.setStandbyBucket(WORKING_INDEX); + assertEquals(WORKING_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(FREQUENT_INDEX); + assertEquals(FREQUENT_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(RARE_INDEX); + assertEquals(RARE_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(RESTRICTED_INDEX); + assertEquals(RESTRICTED_INDEX, job.getEffectiveStandbyBucket()); + + job.setStandbyBucket(NEVER_INDEX); + assertEquals(NEVER_INDEX, job.getEffectiveStandbyBucket()); + } + + @Test public void testModifyingInternalFlags() { final JobInfo jobInfo = new JobInfo.Builder(101, new ComponentName("foo", "bar")) diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java index fb59ea2bb63b..7cc01e1b4292 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java @@ -58,6 +58,7 @@ import android.util.SparseArray; import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; +import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.controllers.PrefetchController.PcConstants; @@ -135,6 +136,9 @@ public class PrefetchControllerTest { when(mJobSchedulerService.getPackagesForUidLocked(anyInt())) .thenAnswer(invocationOnMock -> mPackagesForUid.get(invocationOnMock.getArgument(0))); + // Used in JobStatus. + doReturn(mock(JobSchedulerInternal.class)) + .when(() -> LocalServices.getService(JobSchedulerInternal.class)); // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions // in the past, and PrefetchController sometimes floors values at 0, so if the test time 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 6f713e0fad64..dce162c58d0b 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 @@ -85,6 +85,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.PowerAllowlistInternal; +import com.android.server.job.JobSchedulerInternal; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; @@ -190,6 +191,8 @@ public class QuotaControllerTest { doReturn(mPowerAllowlistInternal) .when(() -> LocalServices.getService(PowerAllowlistInternal.class)); // Used in JobStatus. + doReturn(mock(JobSchedulerInternal.class)) + .when(() -> LocalServices.getService(JobSchedulerInternal.class)); doReturn(mPackageManagerInternal) .when(() -> LocalServices.getService(PackageManagerInternal.class)); // Used in QuotaController.Handler. diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt index cfef0b23b3e7..5fd270ecb2b4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageHelperTestBase.kt @@ -48,6 +48,7 @@ open class PackageHelperTestBase { const val UNINSTALLER_PACKAGE = "com.android.test.known.uninstaller" const val VERIFIER_PACKAGE = "com.android.test.known.verifier" const val PERMISSION_CONTROLLER_PACKAGE = "com.android.test.known.permission" + const val MGMT_ROLE_HOLDER_PACKAGE = "com.android.test.know.device_management" const val TEST_USER_ID = 0 } @@ -119,6 +120,8 @@ open class PackageHelperTestBase { Mockito.doReturn(arrayOf(PERMISSION_CONTROLLER_PACKAGE)).`when`(pms) .getKnownPackageNamesInternal(any(), eq(KnownPackages.PACKAGE_PERMISSION_CONTROLLER), eq(TEST_USER_ID)) + Mockito.doReturn(MGMT_ROLE_HOLDER_PACKAGE).`when`(pms) + .getDevicePolicyManagementRoleHolderPackageName(eq(TEST_USER_ID)) } private fun createPackageManagerService(vararg stageExistingPackages: String): diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt index f9a8ead9cd4a..5cca5fa8ea0b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SuspendPackageHelperTest.kt @@ -128,13 +128,14 @@ class SuspendPackageHelperTest : PackageHelperTestBase() { fun setPackagesSuspended_forQuietMode() { val knownPackages = arrayOf(DEVICE_ADMIN_PACKAGE, DEFAULT_HOME_PACKAGE, DIALER_PACKAGE, INSTALLER_PACKAGE, UNINSTALLER_PACKAGE, VERIFIER_PACKAGE, - PERMISSION_CONTROLLER_PACKAGE) + PERMISSION_CONTROLLER_PACKAGE, MGMT_ROLE_HOLDER_PACKAGE) val failedNames = suspendPackageHelper.setPackagesSuspended(pms.snapshotComputer(), knownPackages, true /* suspended */, null /* appExtras */, null /* launcherExtras */, null /* dialogInfo */, DEVICE_OWNER_PACKAGE, TEST_USER_ID, deviceOwnerUid, true /* forQuietMode */)!! - assertThat(failedNames.size).isEqualTo(0) + assertThat(failedNames.size).isEqualTo(1) + assertThat(failedNames[0]).isEqualTo(MGMT_ROLE_HOLDER_PACKAGE) } @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 51e521d8ffe4..206af5b6aea6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -17,8 +17,10 @@ package com.android.server.wallpaper; import static android.app.WallpaperManager.COMMAND_REAPPLY; +import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.os.FileObserver.CLOSE_WRITE; +import static android.os.UserHandle.MIN_SECONDARY_USER_ID; import static android.os.UserHandle.USER_SYSTEM; import static android.view.Display.DEFAULT_DISPLAY; @@ -106,6 +108,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.List; /** * Tests for the {@link WallpaperManagerService} class. @@ -172,12 +175,12 @@ public class WallpaperManagerServiceTests { sImageWallpaperComponentName = ComponentName.unflattenFromString( sContext.getResources().getString(R.string.image_wallpaper_component)); // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper. - sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext); + sDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(sContext); if (sDefaultWallpaperComponent == null) { sDefaultWallpaperComponent = sImageWallpaperComponentName; doReturn(sImageWallpaperComponentName).when(() -> - WallpaperManager.getCmfDefaultWallpaperComponent(any())); + WallpaperManager.getDefaultWallpaperComponent(any())); } else { sContext.addMockService(sDefaultWallpaperComponent, sWallpaperService); } @@ -262,6 +265,25 @@ public class WallpaperManagerServiceTests { } /** + * Tests that the fundamental fields are set by the main WallpaperData constructor + */ + @Test + public void testWallpaperDataConstructor() { + final int testUserId = MIN_SECONDARY_USER_ID; + for (int which: List.of(FLAG_LOCK, FLAG_SYSTEM)) { + WallpaperData newWallpaperData = new WallpaperData(testUserId, which); + assertEquals(which, newWallpaperData.mWhich); + assertEquals(testUserId, newWallpaperData.userId); + + WallpaperData wallpaperData = mService.getWallpaperSafeLocked(testUserId, which); + assertEquals(wallpaperData.cropFile.getAbsolutePath(), + newWallpaperData.cropFile.getAbsolutePath()); + assertEquals(wallpaperData.wallpaperFile.getAbsolutePath(), + newWallpaperData.wallpaperFile.getAbsolutePath()); + } + } + + /** * Tests that internal basic data should be correct after boot up. */ @Test @@ -405,10 +427,7 @@ public class WallpaperManagerServiceTests { fail("exception occurred while writing system wallpaper attributes"); } - WallpaperData shouldMatchSystem = new WallpaperData(systemWallpaperData.userId, - systemWallpaperData.wallpaperFile.getParentFile(), - systemWallpaperData.wallpaperFile.getAbsolutePath(), - systemWallpaperData.cropFile.getAbsolutePath()); + WallpaperData shouldMatchSystem = new WallpaperData(0, FLAG_SYSTEM); try { TypedXmlPullParser parser = Xml.newBinaryPullParser(); mService.mWallpaperDataParser.parseWallpaperAttributes(parser, shouldMatchSystem, true); diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index cfeaf0b54552..16e1b457ad96 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -114,7 +114,6 @@ android_test { ":SimpleServiceTestApp1", ":SimpleServiceTestApp2", ":SimpleServiceTestApp3", - ":StubTestApp", ":SuspendTestApp", ":MediaButtonReceiverHolderTestHelperApp", "data/broken_shortcut.xml", diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index b304968f3e69..fbb0ca108ecd 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -44,11 +44,6 @@ <option name="teardown-command" value="rm -rf /data/local/tmp/servicestests"/> </target_preparer> - <!-- Load additional APKs onto device --> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> - <option name="push" value="StubTestApp.apk->/data/local/tmp/servicestests/StubTestApp.apk"/> - </target_preparer> - <option name="test-tag" value="FrameworksServicesTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.frameworks.servicestests" /> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 8cfc150afe56..7e638a869682 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -280,6 +280,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test public void testRegisterProxy() throws Exception { + when(mProxyManager.displayBelongsToCaller(anyInt(), anyInt())).thenReturn(true); mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY); verify(mProxyManager).registerProxy(eq(mMockServiceClient), eq(TEST_DISPLAY), anyInt(), eq(mMockSecurityPolicy), @@ -289,9 +290,9 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testRegisterProxyWithoutA11yPermission() throws Exception { + public void testRegisterProxyWithoutA11yPermissionOrRole() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy) - .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + .checkForAccessibilityPermissionOrRole(); assertThrows(SecurityException.class, () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY)); @@ -314,7 +315,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test public void testRegisterProxyForDefaultDisplay() throws Exception { - assertThrows(IllegalArgumentException.class, + assertThrows(SecurityException.class, () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY)); verify(mProxyManager, never()).registerProxy(any(), anyInt(), anyInt(), any(), any(), any(), any()); @@ -332,6 +333,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test public void testUnRegisterProxyWithPermission() throws Exception { + when(mProxyManager.displayBelongsToCaller(anyInt(), anyInt())).thenReturn(true); mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY); mA11yms.unregisterProxyForDisplay(TEST_DISPLAY); @@ -340,9 +342,9 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testUnRegisterProxyWithoutA11yPermission() { + public void testUnRegisterProxyWithoutA11yPermissionOrRole() { doThrow(SecurityException.class).when(mMockSecurityPolicy) - .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + .checkForAccessibilityPermissionOrRole(); assertThrows(SecurityException.class, () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java index b0fd64976c48..c98de7c4d00e 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java @@ -66,6 +66,7 @@ import com.android.server.accessibility.AccessibilityTraceManager; import com.android.server.statusbar.StatusBarManagerInternal; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; @@ -307,6 +308,7 @@ public class WindowMagnificationManagerTest { MagnificationScaleProvider.MAX_SCALE); } + @Ignore("b/278816260: We could refer to b/182561174#comment4 for solution.") @Test public void logTrackingTypingFocus_processScroll_logDuration() { WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 4268eb924225..662477ddbbe9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -18,6 +18,9 @@ package com.android.server.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON; +import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED; +import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED; import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; @@ -32,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyObject; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -45,6 +49,7 @@ import android.app.trust.ITrustManager; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricManager.Authenticators; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -64,7 +69,10 @@ import android.security.KeyStore; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricFrameworkStatsLogger; +import com.android.server.biometrics.log.OperationContextExt; import org.junit.Before; import org.junit.Test; @@ -95,6 +103,7 @@ public class AuthSessionTest { @Mock private IBiometricSysuiReceiver mSysuiReceiver; @Mock private KeyStore mKeyStore; @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver; + @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger; private Random mRandom; private IBinder mToken; @@ -395,6 +404,84 @@ public class AuthSessionTest { eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE), eq(acquiredStrVendor)); } + @Test + public void testLogOnDialogDismissed_authenticatedWithConfirmation() throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + + session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null); + verify(mBiometricFrameworkStatsLogger, times(1)).authenticate( + (OperationContextExt) anyObject(), + eq(BiometricsProtoEnums.MODALITY_FACE), + eq(BiometricsProtoEnums.ACTION_UNKNOWN), + eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT), + eq(false), /* debugEnabled */ + anyLong(), /* latency */ + eq(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED), + eq(true), /* confirmationRequired */ + eq(0) /* userId */, + eq(-1f) /* ambientLightLux */); + } + + @Test + public void testLogOnDialogDismissed_authenticatedWithoutConfirmation() throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + + session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null); + verify(mBiometricFrameworkStatsLogger, never()).authenticate( + anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(), + anyBoolean(), anyInt(), eq(-1f)); + verify(mBiometricFrameworkStatsLogger, never()).error( + anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(), + anyInt(), anyInt()); + } + + @Test + public void testLogOnDialogDismissed_error() throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + + session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null); + verify(mBiometricFrameworkStatsLogger, times(1)).error( + (OperationContextExt) anyObject(), + eq(BiometricsProtoEnums.MODALITY_FACE), + eq(BiometricsProtoEnums.ACTION_AUTHENTICATE), + eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT), + eq(false), + anyLong(), + eq(BIOMETRIC_ERROR_NEGATIVE_BUTTON), + eq(0) /* vendorCode */, + eq(0) /* userId */); + } + // TODO (b/208484275) : Enable these tests // @Test // public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception { @@ -498,7 +585,7 @@ public class AuthSessionTest { return new AuthSession(mContext, mBiometricContext, mStatusBarService, mSysuiReceiver, mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, - false /* debugEnabled */, mFingerprintSensorProps); + false /* debugEnabled */, mFingerprintSensorProps, mBiometricFrameworkStatsLogger); } private PromptInfo createPromptInfo(@Authenticators.Types int authenticators) { diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java index 8a107ada16b0..dccc26af8024 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CallMetadataSyncDataTest.java @@ -18,7 +18,7 @@ package com.android.server.companion.datatransfer.contextsync; import static com.google.common.truth.Truth.assertThat; -import android.os.Parcel; +import android.os.Bundle; import android.testing.AndroidTestingRunner; import org.junit.Test; @@ -28,7 +28,7 @@ import org.junit.runner.RunWith; public class CallMetadataSyncDataTest { @Test - public void call_writeToParcel_fromParcel_reconstructsSuccessfully() { + public void call_writeToBundle_fromBundle_reconstructsSuccessfully() { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); final String id = "5"; final String callerId = "callerId"; @@ -36,6 +36,7 @@ public class CallMetadataSyncDataTest { final String appName = "appName"; final String appIdentifier = "com.google.test"; final int status = 1; + final int direction = android.companion.Telecom.Call.OUTGOING; final int control1 = 2; final int control2 = 3; call.setId(id); @@ -45,14 +46,13 @@ public class CallMetadataSyncDataTest { new CallMetadataSyncData.CallFacilitator(appName, appIdentifier); call.setFacilitator(callFacilitator); call.setStatus(status); + call.setDirection(direction); call.addControl(control1); call.addControl(control2); - Parcel parcel = Parcel.obtain(); - call.writeToParcel(parcel, /* flags= */ 0); - parcel.setDataPosition(0); - final CallMetadataSyncData.Call reconstructedCall = CallMetadataSyncData.Call.fromParcel( - parcel); + final Bundle bundle = call.writeToBundle(); + final CallMetadataSyncData.Call reconstructedCall = CallMetadataSyncData.Call.fromBundle( + bundle); assertThat(reconstructedCall.getId()).isEqualTo(id); assertThat(reconstructedCall.getCallerId()).isEqualTo(callerId); @@ -60,6 +60,7 @@ public class CallMetadataSyncDataTest { assertThat(reconstructedCall.getFacilitator().getName()).isEqualTo(appName); assertThat(reconstructedCall.getFacilitator().getIdentifier()).isEqualTo(appIdentifier); assertThat(reconstructedCall.getStatus()).isEqualTo(status); + assertThat(reconstructedCall.getDirection()).isEqualTo(direction); assertThat(reconstructedCall.getControls()).containsExactly(control1, control2); } } diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java index 6a939ab34768..201d8f9413a2 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceCallTest.java @@ -149,6 +149,27 @@ public class CrossDeviceCallTest { } @Test + public void updateCallDetails_transitionDialingToOngoing() { + final CrossDeviceCall crossDeviceCall = new CrossDeviceCall( + InstrumentationRegistry.getTargetContext(), + mUninitializedCallDetails, /* callAudioState= */ null); + crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_DIALING, + Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE)); + assertWithMessage("Wrong status for dialing state").that(crossDeviceCall.getStatus()) + .isEqualTo(android.companion.Telecom.Call.DIALING); + assertWithMessage("Wrong controls for dialing state").that(crossDeviceCall.getControls()) + .isEqualTo(Set.of(android.companion.Telecom.END)); + crossDeviceCall.updateCallDetails(createCallDetails(Call.STATE_ACTIVE, + Call.Details.CAPABILITY_HOLD | Call.Details.CAPABILITY_MUTE)); + assertWithMessage("Wrong status for active state").that(crossDeviceCall.getStatus()) + .isEqualTo(android.companion.Telecom.Call.ONGOING); + assertWithMessage("Wrong controls for active state").that(crossDeviceCall.getControls()) + .isEqualTo(Set.of(android.companion.Telecom.END, + android.companion.Telecom.MUTE, + android.companion.Telecom.PUT_ON_HOLD)); + } + + @Test public void updateSilencedIfRinging_ringing_silenced() { final CrossDeviceCall crossDeviceCall = new CrossDeviceCall( InstrumentationRegistry.getTargetContext(), diff --git a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java index 7688fcb2faba..7e392a4261c1 100644 --- a/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/datatransfer/contextsync/CrossDeviceSyncControllerTest.java @@ -162,13 +162,31 @@ public class CrossDeviceSyncControllerTest { } @Test - public void createPhoneAccount_success() { + public void createPhoneAccount_sip_success() { final PhoneAccount phoneAccount = CrossDeviceSyncController.PhoneAccountManager.createPhoneAccount( new PhoneAccountHandle( new ComponentName("com.google.test", "com.google.test.Activity"), - "id"), "Test App", "com.google.test"); + "id"), "Test App", "com.google.test", 1, false); assertWithMessage("Could not create phone account").that(phoneAccount).isNotNull(); + assertWithMessage("Wrong schemes supported; should not support TEL") + .that(phoneAccount.supportsUriScheme(PhoneAccount.SCHEME_TEL)).isFalse(); + assertWithMessage("Wrong schemes supported; should support SIP") + .that(phoneAccount.supportsUriScheme(PhoneAccount.SCHEME_SIP)).isTrue(); + } + + @Test + public void createPhoneAccount_tel_success() { + final PhoneAccount phoneAccount = + CrossDeviceSyncController.PhoneAccountManager.createPhoneAccount( + new PhoneAccountHandle( + new ComponentName("com.google.test", "com.google.test.Activity"), + "id"), "Test App", "com.google.test", 1, true); + assertWithMessage("Could not create phone account").that(phoneAccount).isNotNull(); + assertWithMessage("Wrong schemes supported; should support TEL") + .that(phoneAccount.supportsUriScheme(PhoneAccount.SCHEME_TEL)).isTrue(); + assertWithMessage("Wrong schemes supported; should not support SIP") + .that(phoneAccount.supportsUriScheme(PhoneAccount.SCHEME_SIP)).isFalse(); } @Test @@ -214,9 +232,10 @@ public class CrossDeviceSyncControllerTest { } @Test - public void updateCalls_newCall() { + public void updateCalls_newIncomingCall_added() { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.INCOMING); call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); @@ -225,25 +244,133 @@ public class CrossDeviceSyncControllerTest { new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); verify(mMockTelecomManager, times(1)).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); + } + + @Test + public void updateCalls_newOutgoingCall_added() { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.OUTGOING); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setCallerId("555-555-5555"); + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + callMetadataSyncData.addCall(call); + final CrossDeviceSyncController.CallManager callManager = + new CrossDeviceSyncController.CallManager(mMockContext, + new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); + callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, times(1)).placeCall(any(), any()); + } + + @Test + public void updateCalls_newOutgoingCall_noAddress_notAdded() { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.OUTGOING); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + callMetadataSyncData.addCall(call); + final CrossDeviceSyncController.CallManager callManager = + new CrossDeviceSyncController.CallManager(mMockContext, + new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); + callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); } @Test - public void updateCalls_newCall_noFacilitator() { + public void updateCalls_newIncomingCall_noFacilitator_notAdded() { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.INCOMING); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); final CrossDeviceSyncController.CallManager callManager = new CrossDeviceSyncController.CallManager(mMockContext, new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); - verify(mMockTelecomManager, times(0)).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); } @Test - public void updateCalls_existingCall() { + public void updateCalls_newOutgoingCall_noFacilitator_notAdded() { final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.OUTGOING); + call.setCallerId("555-555-5555"); + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + callMetadataSyncData.addCall(call); + final CrossDeviceSyncController.CallManager callManager = + new CrossDeviceSyncController.CallManager(mMockContext, + new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); + callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); + } + + @Test + public void updateCalls_newIncomingCall_isSelfOwned_notAdded() { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + call.setId("123abc::originalId"); + call.setDirection(android.companion.Telecom.Call.INCOMING); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + callMetadataSyncData.addCall(call); + final CrossDeviceSyncController.CallManager callManager = + new CrossDeviceSyncController.CallManager(mMockContext, + new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); + callManager.addSelfOwnedCallId("originalId"); + callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); + } + + + @Test + public void updateCalls_newOutgoingCall_isSelfOwned_notAdded() { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + call.setId("123abc::originalId"); + call.setDirection(android.companion.Telecom.Call.OUTGOING); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setCallerId("555-555-5555"); + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + callMetadataSyncData.addCall(call); + final CrossDeviceSyncController.CallManager callManager = + new CrossDeviceSyncController.CallManager(mMockContext, + new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); + callManager.addSelfOwnedCallId("originalId"); + callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); + } + + @Test + public void updateCalls_existingIncomingCall() { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.INCOMING); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); + callMetadataSyncData.addCall(call); + final CrossDeviceSyncController.CallManager callManager = + new CrossDeviceSyncController.CallManager(mMockContext, + new CrossDeviceSyncController.PhoneAccountManager(mMockContext)); + callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId())); + callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); + verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); + } + + @Test + public void updateCalls_existingOutgoingCall() { + final CallMetadataSyncData.Call call = new CallMetadataSyncData.Call(); + call.setId("123abc"); + call.setDirection(android.companion.Telecom.Call.OUTGOING); + call.setFacilitator(new CallMetadataSyncData.CallFacilitator("name", "com.android.test")); + call.setCallerId("555-555-5555"); final CallMetadataSyncData callMetadataSyncData = new CallMetadataSyncData(); callMetadataSyncData.addCall(call); final CrossDeviceSyncController.CallManager callManager = @@ -252,6 +379,7 @@ public class CrossDeviceSyncControllerTest { callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId())); callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); } @Test @@ -266,6 +394,7 @@ public class CrossDeviceSyncControllerTest { callManager.mCallIds.put(/* associationId= */ 0, Set.of(call.getId(), "fakeCallId")); callManager.updateCalls(/* associationId= */ 0, callMetadataSyncData); verify(mMockTelecomManager, never()).addNewIncomingCall(any(), any()); + verify(mMockTelecomManager, never()).placeCall(any(), any()); assertWithMessage("Hasn't removed the id of the removed call") .that(callManager.mCallIds) .containsExactly(/* associationId= */ 0, Set.of(call.getId())); diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS b/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS new file mode 100644 index 000000000000..24561c59bba6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentprotection/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 544200 + +include /core/java/android/view/contentcapture/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java index b46f1a61c745..50ea48719948 100644 --- a/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java +++ b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java @@ -15,14 +15,27 @@ */ package com.android.server.credentials; +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.credentials.metrics.BrowsedAuthenticationMetric; +import com.android.server.credentials.metrics.CandidateAggregateMetric; +import com.android.server.credentials.metrics.CandidateBrowsingPhaseMetric; +import com.android.server.credentials.metrics.ChosenProviderFinalPhaseMetric; import com.android.server.credentials.metrics.InitialPhaseMetric; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.security.cert.CertificateException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Given the secondary-system nature of the MetricUtilities, we expect absolutely nothing to * throw an error. If one presents itself, that is problematic. @@ -33,6 +46,11 @@ import org.junit.runner.RunWith; @SmallTest public final class MetricUtilitiesTest { + @Before + public void setUp() throws CertificateException { + final Context context = ApplicationProvider.getApplicationContext(); + } + @Test public void logApiCalledInitialPhase_nullInitPhaseMetricAndNegativeSequence_success() { MetricUtilities.logApiCalledInitialPhase(null, -1); @@ -49,4 +67,102 @@ public final class MetricUtilitiesTest { MetricUtilities.getHighlyUniqueInteger()); MetricUtilities.logApiCalledInitialPhase(validInitPhaseMetric, 1); } + + @Test + public void logApiCalledTotalCandidate_nullCandidateNegativeSequence_success() { + MetricUtilities.logApiCalledAggregateCandidate(null, -1); + } + + @Test + public void logApiCalledTotalCandidate_invalidCandidatePhasePositiveSequence_success() { + MetricUtilities.logApiCalledAggregateCandidate(new CandidateAggregateMetric(-1), 1); + } + + @Test + public void logApiCalledTotalCandidate_validPhaseMetric_success() { + MetricUtilities.logApiCalledAggregateCandidate( + new CandidateAggregateMetric(MetricUtilities.getHighlyUniqueInteger()), 1); + } + + @Test + public void logApiCalledNoUidFinal_nullNoUidFinalNegativeSequenceAndStatus_success() { + MetricUtilities.logApiCalledNoUidFinal(null, null, + -1, -1); + } + + @Test + public void logApiCalledNoUidFinal_invalidNoUidFinalPhasePositiveSequenceAndStatus_success() { + MetricUtilities.logApiCalledNoUidFinal(new ChosenProviderFinalPhaseMetric(-1, -1), + List.of(new CandidateBrowsingPhaseMetric()), 1, 1); + } + + @Test + public void logApiCalledNoUidFinal_validNoUidFinalMetric_success() { + MetricUtilities.logApiCalledNoUidFinal( + new ChosenProviderFinalPhaseMetric(MetricUtilities.getHighlyUniqueInteger(), + MetricUtilities.getHighlyUniqueInteger()), + List.of(new CandidateBrowsingPhaseMetric()), 1, 1); + } + + @Test + public void logApiCalledCandidate_nullMapNullInitFinalNegativeSequence_success() { + MetricUtilities.logApiCalledCandidatePhase(null, -1, + null); + } + + @Test + public void logApiCalledCandidate_invalidProvidersCandidatePositiveSequence_success() { + Map<String, ProviderSession> testMap = new HashMap<>(); + testMap.put("s", null); + MetricUtilities.logApiCalledCandidatePhase(testMap, 1, + null); + } + + @Test + public void logApiCalledCandidateGet_nullMapFinalNegativeSequence_success() { + MetricUtilities.logApiCalledCandidateGetMetric(null, -1); + } + + @Test + public void logApiCalledCandidateGet_invalidProvidersCandidatePositiveSequence_success() { + Map<String, ProviderSession> testMap = new HashMap<>(); + testMap.put("s", null); + MetricUtilities.logApiCalledCandidateGetMetric(testMap, 1); + } + + @Test + public void logApiCalledAuthMetric_nullAuthMetricNegativeSequence_success() { + MetricUtilities.logApiCalledAuthenticationMetric(null, -1); + } + + @Test + public void logApiCalledAuthMetric_invalidAuthMetricPositiveSequence_success() { + MetricUtilities.logApiCalledAuthenticationMetric(new BrowsedAuthenticationMetric(-1), 1); + } + + @Test + public void logApiCalledAuthMetric_nullAuthMetricPositiveSequence_success() { + MetricUtilities.logApiCalledAuthenticationMetric( + new BrowsedAuthenticationMetric(MetricUtilities.getHighlyUniqueInteger()), -1); + } + + @Test + public void logApiCalledFinal_nullFinalNegativeSequenceAndStatus_success() { + MetricUtilities.logApiCalledFinalPhase(null, null, + -1, -1); + } + + @Test + public void logApiCalledFinal_invalidFinalPhasePositiveSequenceAndStatus_success() { + MetricUtilities.logApiCalledFinalPhase(new ChosenProviderFinalPhaseMetric(-1, -1), + List.of(new CandidateBrowsingPhaseMetric()), 1, 1); + } + + @Test + public void logApiCalledFinal_validFinalMetric_success() { + MetricUtilities.logApiCalledFinalPhase( + new ChosenProviderFinalPhaseMetric(MetricUtilities.getHighlyUniqueInteger(), + MetricUtilities.getHighlyUniqueInteger()), + List.of(new CandidateBrowsingPhaseMetric()), 1, 1); + } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 39de2cf86a6c..99a3b808e082 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -7842,7 +7842,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { throws Exception { int validLockTaskFeatures = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS - | LOCK_TASK_FEATURE_NOTIFICATIONS; + | LOCK_TASK_FEATURE_NOTIFICATIONS | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK; setDeviceOwner(); dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); @@ -7857,7 +7857,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { public void testSetLockTaskFeatures_financeDo_invalidLockTaskFeatures_throwsException() throws Exception { int invalidLockTaskFeatures = LOCK_TASK_FEATURE_NONE | LOCK_TASK_FEATURE_OVERVIEW - | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK; + | LOCK_TASK_FEATURE_HOME; setDeviceOwner(); dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); // Called during setup. diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index a6c57371803b..0e775d57bbd7 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -35,12 +35,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -72,6 +74,7 @@ import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionManager; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.MessageQueue; @@ -96,17 +99,18 @@ import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; +import com.android.server.display.DisplayManagerService.DeviceStateListener; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.input.InputManagerInternal; import com.android.server.lights.LightsManager; import com.android.server.sensors.SensorManagerInternal; import com.android.server.wm.WindowManagerInternal; -import com.google.common.collect.ImmutableMap; - import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; +import com.google.common.collect.ImmutableMap; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -114,6 +118,7 @@ import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; @@ -547,6 +552,30 @@ public class DisplayManagerServiceTest { } /** + * Tests that we send the device state to window manager + */ + @Test + public void testOnStateChanged_sendsStateChangedEventToWm() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + registerDefaultDisplays(displayManager); + displayManager.windowManagerAndInputReady(); + displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + DeviceStateListener listener = displayManager.new DeviceStateListener(); + Handler handler = displayManager.getDisplayHandler(); + IDisplayManagerCallback displayChangesCallback = registerDisplayChangeCallback( + displayManager); + + listener.onStateChanged(123); + waitForIdleHandler(handler); + + InOrder inOrder = inOrder(mMockWindowManagerInternal, displayChangesCallback); + // Verify there are no display events before WM call + inOrder.verify(displayChangesCallback, never()).onDisplayEvent(anyInt(), anyInt()); + inOrder.verify(mMockWindowManagerInternal).onDisplayManagerReceivedDeviceState(123); + } + + /** * Tests that there should be a display change notification to WindowManager to update its own * internal state for things like display cutout when nonOverrideDisplayInfo is changed. */ @@ -2054,6 +2083,15 @@ public class DisplayManagerServiceTest { updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo); } + private IDisplayManagerCallback registerDisplayChangeCallback( + DisplayManagerService displayManager) { + IDisplayManagerCallback displayChangesCallback = mock(IDisplayManagerCallback.class); + when(displayChangesCallback.asBinder()).thenReturn(new Binder()); + DisplayManagerService.BinderService binderService = displayManager.new BinderService(); + binderService.registerCallback(displayChangesCallback); + return displayChangesCallback; + } + private FakeDisplayManagerCallback registerDisplayListenerCallback( DisplayManagerService displayManager, DisplayManagerService.BinderService displayManagerBinderService, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java new file mode 100644 index 000000000000..920c376d5dfb --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTvTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.hardware.hdmi.HdmiDeviceInfo; +import android.os.Looper; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Collections; + +/** + * TV specific tests for {@link HdmiControlService} class. + */ +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class HdmiControlServiceTvTest { + + private static final String TAG = "HdmiControlServiceTvTest"; + private HdmiControlService mHdmiControlService; + private HdmiCecController mHdmiCecController; + private FakeNativeWrapper mNativeWrapper; + private HdmiEarcController mHdmiEarcController; + private FakeEarcNativeWrapper mEarcNativeWrapper; + private Looper mMyLooper; + private TestLooper mTestLooper = new TestLooper(); + + @Before + public void setUp() throws Exception { + Context context = InstrumentationRegistry.getTargetContext(); + mMyLooper = mTestLooper.getLooper(); + + FakeAudioFramework audioFramework = new FakeAudioFramework(); + + mHdmiControlService = + new HdmiControlService(InstrumentationRegistry.getTargetContext(), + Collections.singletonList(HdmiDeviceInfo.DEVICE_TV), + audioFramework.getAudioManager(), + audioFramework.getAudioDeviceVolumeManager()) { + @Override + int pathToPortId(int path) { + return Constants.INVALID_PORT_ID + 1; + } + }; + + mMyLooper = mTestLooper.getLooper(); + mHdmiControlService.setIoLooper(mMyLooper); + mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context)); + mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper()); + mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY); + + mNativeWrapper = new FakeNativeWrapper(); + mHdmiCecController = HdmiCecController.createWithNativeWrapper( + mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter()); + mHdmiControlService.setCecController(mHdmiCecController); + mEarcNativeWrapper = new FakeEarcNativeWrapper(); + mHdmiEarcController = HdmiEarcController.createWithNativeWrapper( + mHdmiControlService, mEarcNativeWrapper); + mHdmiControlService.setEarcController(mHdmiEarcController); + mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create( + mHdmiControlService)); + mHdmiControlService.initService(); + + mTestLooper.dispatchAll(); + } + + @Test + public void onCecMessage_shortPhysicalAddress_featureAbortInvalidOperand() { + // Invalid <Inactive Source> message. + HdmiCecMessage message = HdmiUtils.buildMessage("40:9D:14"); + + mNativeWrapper.onCecMessage(message); + mTestLooper.dispatchAll(); + + HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand( + Constants.ADDR_TV, Constants.ADDR_PLAYBACK_1, Constants.MESSAGE_INACTIVE_SOURCE, + Constants.ABORT_INVALID_OPERAND); + assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort); + } + + @Test + public void handleCecCommand_shortPhysicalAddress_returnsAbortInvalidOperand() { + // Invalid <Active Source> message. + HdmiCecMessage message = HdmiUtils.buildMessage("4F:82:10"); + + // In case of a broadcasted message <Feature Abort> is not expected. + // See CEC 1.4b specification, 12.2 Protocol General Rules for detail. + assertThat(mHdmiControlService.handleCecCommand(message)) + .isEqualTo(Constants.ABORT_INVALID_OPERAND); + } + + @Test + public void test_verifyPhysicalAddresses() { + // <Routing Change> + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("0F:80:10:00:40:00"))).isTrue(); + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("0F:80:10:00:40"))).isFalse(); + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("0F:80:10"))).isFalse(); + + // <System Audio Mode Request> + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("40:70:00:00"))).isTrue(); + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("40:70:00"))).isFalse(); + + // <Active Source> + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("4F:82:10:00"))).isTrue(); + assertThat(mHdmiControlService + .verifyPhysicalAddresses(HdmiUtils.buildMessage("4F:82:10"))).isFalse(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 7aec04568e0f..5c6164efb3b6 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -18,17 +18,12 @@ package com.android.server.power; import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_ERRORED; import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; - import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; @@ -47,6 +42,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -54,13 +50,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; -import android.app.AppOpsManager; import android.attention.AttentionManagerInternal; +import android.compat.testing.PlatformCompatChangeRule; +import android.content.AttributionSource; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.PackageManager; +import android.content.PermissionChecker; import android.content.res.Resources; import android.hardware.SensorManager; import android.hardware.display.AmbientDisplayConfiguration; @@ -98,7 +95,6 @@ import com.android.server.lights.LightsManager; import com.android.server.policy.WindowManagerPolicy; import com.android.server.power.PowerManagerService.BatteryReceiver; import com.android.server.power.PowerManagerService.BinderService; -import com.android.server.power.PowerManagerService.Injector; import com.android.server.power.PowerManagerService.NativeWrapper; import com.android.server.power.PowerManagerService.UserSwitchedReceiver; import com.android.server.power.PowerManagerService.WakeLock; @@ -108,9 +104,14 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.testutils.OffsettableClock; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; import org.mockito.Mock; @@ -148,18 +149,18 @@ public class PowerManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternalMock; @Mock private AttentionManagerInternal mAttentionManagerInternalMock; @Mock private DreamManagerInternal mDreamManagerInternalMock; - @Mock private WindowManagerPolicy mPolicyMock; @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; @Mock private Notifier mNotifierMock; @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; @Mock private SystemPropertiesWrapper mSystemPropertiesMock; - @Mock private AppOpsManager mAppOpsManagerMock; @Mock private LowPowerStandbyController mLowPowerStandbyControllerMock; @Mock private Callable<Void> mInvalidateInteractiveCachesMock; + @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; + @Mock private PowerManagerService.PermissionCheckerWrapper mPermissionCheckerWrapperMock; + @Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper; - @Mock - private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; + @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); private PowerManagerService mService; private ContextWrapper mContextSpy; @@ -209,7 +210,6 @@ public class PowerManagerServiceTest { .thenReturn(true); when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true); - when(mPolicyMock.getLidState()).thenReturn(LID_ABSENT); addLocalServiceMock(LightsManager.class, mLightsManagerMock); addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock); @@ -217,7 +217,6 @@ public class PowerManagerServiceTest { addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock); - addLocalServiceMock(WindowManagerPolicy.class, mPolicyMock); mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); mResourcesSpy = spy(mContextSpy.getResources()); @@ -237,7 +236,7 @@ public class PowerManagerServiceTest { } private PowerManagerService createService() { - mService = new PowerManagerService(mContextSpy, new Injector() { + mService = new PowerManagerService(mContextSpy, new PowerManagerService.Injector() { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, @@ -333,8 +332,13 @@ public class PowerManagerServiceTest { } @Override - AppOpsManager createAppOpsManager(Context context) { - return mAppOpsManagerMock; + PowerManagerService.PermissionCheckerWrapper createPermissionCheckerWrapper() { + return mPermissionCheckerWrapperMock; + } + + @Override + PowerManagerService.PowerPropertiesWrapper createPowerPropertiesWrapper() { + return mPowerPropertiesWrapper; } }); return mService; @@ -595,6 +599,7 @@ public class PowerManagerServiceTest { } @Test + @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnAllowed() { createService(); startSystem(); @@ -603,11 +608,12 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; - when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, - Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED); - when(mContextSpy.checkCallingOrSelfPermission( - android.Manifest.permission.TURN_SCREEN_ON)).thenReturn( - PackageManager.PERMISSION_GRANTED); + AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), + packageName, /* attributionTag= */ null); + + doReturn(PermissionChecker.PERMISSION_GRANTED).when( + mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), + eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); // First, ensure that a normal full wake lock does not cause a wakeup int flags = PowerManager.FULL_WAKE_LOCK; @@ -632,6 +638,35 @@ public class PowerManagerServiceTest { } @Test + @DisableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) + public void testWakefulnessAwake_AcquireCausesWakeupOldSdk_turnScreenOnAllowed() { + createService(); + startSystem(); + forceSleep(); + + IBinder token = new Binder(); + String tag = "acq_causes_wakeup"; + String packageName = "pkg.name"; + AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), + packageName, /* attributionTag= */ null); + + // verify that the wakeup is allowed for apps targeting older sdks, and therefore won't have + // the TURN_SCREEN_ON permission granted + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), + eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); + + doReturn(false).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); + + int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); + mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); + } + + @Test + @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) public void testWakefulnessAwake_AcquireCausesWakeup_turnScreenOnDenied() { createService(); startSystem(); @@ -640,30 +675,43 @@ public class PowerManagerServiceTest { IBinder token = new Binder(); String tag = "acq_causes_wakeup"; String packageName = "pkg.name"; - when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, - Binder.getCallingUid(), packageName)).thenReturn(MODE_ERRORED); + AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), + packageName, /* attributionTag= */ null); + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), + eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); + doReturn(false).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); + doReturn(false).when(mPowerPropertiesWrapper).permissionless_turn_screen_on(); - // Verify that flag has no effect when OP_TURN_SCREEN_ON is not allowed + // Verify that flag has no effect when TURN_SCREEN_ON is not allowed for apps targeting U+ int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); - if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { - assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); - } else { - assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); - } + assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); + } - when(mAppOpsManagerMock.checkOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, - Binder.getCallingUid(), packageName)).thenReturn(MODE_ALLOWED); - when(mContextSpy.checkCallingOrSelfPermission( - android.Manifest.permission.TURN_SCREEN_ON)).thenReturn( - PackageManager.PERMISSION_DENIED); + @Test + @EnableCompatChanges({PowerManagerService.REQUIRE_TURN_SCREEN_ON_PERMISSION}) + public void testWakefulnessAwake_AcquireCausesWakeupOldSdk_turnScreenOnDenied() { + createService(); + startSystem(); + forceSleep(); - // Verify that the flag has no effect when OP_TURN_SCREEN_ON is allowed but - // android.permission.TURN_SCREEN_ON is denied - flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; + IBinder token = new Binder(); + String tag = "acq_causes_wakeup"; + String packageName = "pkg.name"; + AttributionSource attrSrc = new AttributionSource(Binder.getCallingUid(), + packageName, /* attributionTag= */ null); + doReturn(PermissionChecker.PERMISSION_HARD_DENIED).when( + mPermissionCheckerWrapperMock).checkPermissionForDataDelivery(any(), + eq(android.Manifest.permission.TURN_SCREEN_ON), anyInt(), eq(attrSrc), anyString()); + + doReturn(true).when(mPowerPropertiesWrapper).waive_target_sdk_check_for_turn_screen_on(); + + // Verify that flag has no effect when TURN_SCREEN_ON is not allowed for apps targeting U+ + int flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, null /* workSource */, null /* historyTag */, Display.INVALID_DISPLAY, null); if (PowerProperties.permissionless_turn_screen_on().orElse(false)) { @@ -684,20 +732,6 @@ public class PowerManagerServiceTest { assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE); } - @Test - public void testWakefulnessAwake_ShouldNotWakeUpWhenLidClosed() { - when(mPolicyMock.getLidState()).thenReturn(LID_CLOSED); - createService(); - startSystem(); - forceSleep(); - - mService.getBinderServiceInstance().wakeUp(mClock.now(), - PowerManager.WAKE_REASON_POWER_BUTTON, - "testing IPowerManager.wakeUp()", "pkg.name"); - - assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_ASLEEP); - } - /** * Tests a series of variants that control whether a device wakes-up when it is plugged in * or docked. diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index f44c1d18614d..4315254f68a9 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" /> + <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.MANAGE_USERS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" /> @@ -36,6 +37,7 @@ <uses-permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" /> <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> 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 9166b3d75f60..cebc5409c795 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -61,6 +61,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Build.VERSION_CODES.P; +import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.UserHandle.USER_SYSTEM; import static android.os.UserManager.USER_TYPE_FULL_SECONDARY; import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; @@ -80,6 +81,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; @@ -119,12 +121,14 @@ import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import android.Manifest; +import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.ActivityManager; @@ -181,11 +185,14 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.Parcel; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.WorkSource; import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.MediaStore; @@ -351,6 +358,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private PermissionManager mPermissionManager; @Mock private DevicePolicyManagerInternal mDevicePolicyManager; + @Mock + private PowerManager mPowerManager; + private final ArrayList<WakeLock> mAcquiredWakeLocks = new ArrayList<>(); private final TestPostNotificationTrackerFactory mPostNotificationTrackerFactory = new TestPostNotificationTrackerFactory(); @@ -431,8 +441,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final List<PostNotificationTracker> mCreatedTrackers = new ArrayList<>(); @Override - public PostNotificationTracker newTracker() { - PostNotificationTracker tracker = PostNotificationTrackerFactory.super.newTracker(); + public PostNotificationTracker newTracker(@Nullable WakeLock optionalWakeLock) { + PostNotificationTracker tracker = PostNotificationTrackerFactory.super.newTracker( + optionalWakeLock); mCreatedTrackers.add(tracker); return tracker; } @@ -563,6 +574,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true); + // Use the real PowerManager to back up the mock w.r.t. creating WakeLocks. + // This is because 1) we need a mock to verify() calls and tracking the created WakeLocks, + // but 2) PowerManager and WakeLock perform their own checks (e.g. correct arguments, don't + // call release twice, etc) and we want the test to fail if such misuse happens, too. + PowerManager realPowerManager = mContext.getSystemService(PowerManager.class); + when(mPowerManager.newWakeLock(anyInt(), anyString())).then( + (Answer<WakeLock>) invocation -> { + WakeLock wl = realPowerManager.newWakeLock(invocation.getArgument(0), + invocation.getArgument(1)); + mAcquiredWakeLocks.add(wl); + return wl; + }); + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false); + // apps allowed as convos mService.setStringArrayResourceValue(PKG_O); @@ -579,7 +606,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), mTelecomManager, mLogger, mTestFlagResolver, mPermissionManager, - mPostNotificationTrackerFactory); + mPowerManager, mPostNotificationTrackerFactory); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -686,6 +713,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @After + public void assertAllWakeLocksReleased() { + for (WakeLock wakeLock : mAcquiredWakeLocks) { + assertThat(wakeLock.isHeld()).isFalse(); + } + } + + @After public void tearDown() throws Exception { if (mFile != null) mFile.delete(); clearDeviceConfig(); @@ -1486,7 +1520,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -1507,7 +1541,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -1803,6 +1837,128 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void enqueueNotification_acquiresAndReleasesWakeLock() throws Exception { + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_acquiresAndReleasesWakeLock", 0, + generateNotificationRecord(null).getNotification(), 0); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isTrue(); + + waitForIdle(); + + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_throws_acquiresAndReleasesWakeLock() throws Exception { + // Simulate not enqueued due to rejected inputs. + assertThrows(Exception.class, + () -> mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_throws_acquiresAndReleasesWakeLock", 0, + /* notification= */ null, 0)); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_notEnqueued_acquiresAndReleasesWakeLock() throws Exception { + // Simulate not enqueued due to snoozing inputs. + when(mSnoozeHelper.getSnoozeContextForUnpostedNotification(anyInt(), any(), any())) + .thenReturn("zzzzzzz"); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_notEnqueued_acquiresAndReleasesWakeLock", 0, + generateNotificationRecord(null).getNotification(), 0); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isTrue(); + + waitForIdle(); + + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_notPosted_acquiresAndReleasesWakeLock() throws Exception { + // Simulate enqueued but not posted due to missing small icon. + Notification notif = new Notification.Builder(mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .build(); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_notPosted_acquiresAndReleasesWakeLock", 0, + notif, 0); + + verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isTrue(); + + waitForIdle(); + + // NLSes were not called. + verify(mListeners, never()).prepareNotifyPostedLocked(any(), any(), anyBoolean()); + + assertThat(mAcquiredWakeLocks).hasSize(1); + assertThat(mAcquiredWakeLocks.get(0).isHeld()).isFalse(); + } + + @Test + public void enqueueNotification_setsWakeLockWorkSource() throws Exception { + // Use a "full" mock for the PowerManager (instead of the one that delegates to the real + // service) so we can return a mocked WakeLock that we can verify() on. + reset(mPowerManager); + WakeLock wakeLock = mock(WakeLock.class); + when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(wakeLock); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + InOrder inOrder = inOrder(mPowerManager, wakeLock); + inOrder.verify(mPowerManager).newWakeLock(eq(PARTIAL_WAKE_LOCK), anyString()); + inOrder.verify(wakeLock).setWorkSource(eq(new WorkSource(mUid, PKG))); + inOrder.verify(wakeLock).acquire(anyLong()); + inOrder.verify(wakeLock).release(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void enqueueNotification_wakeLockSystemPropertyOff_noWakeLock() throws Exception { + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + verifyZeroInteractions(mPowerManager); + } + + @Test + public void enqueueNotification_wakeLockDeviceConfigOff_noWakeLock() throws Exception { + mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "false", false); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "enqueueNotification_setsWakeLockWorkSource", 0, + generateNotificationRecord(null).getNotification(), 0); + waitForIdle(); + + verifyZeroInteractions(mPowerManager); + } + + @Test public void testCancelNonexistentNotification() throws Exception { mBinderService.cancelNotificationWithTag(PKG, PKG, "testCancelNonexistentNotification", 0, 0); @@ -4361,7 +4517,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4380,7 +4536,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4400,7 +4556,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4420,7 +4576,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), - update.getUid(), mPostNotificationTrackerFactory.newTracker()); + update.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -4434,13 +4590,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); r = generateNotificationRecord(mTestNotificationChannel, 1, null, false); r.setCriticality(CriticalNotificationExtractor.CRITICAL); runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); mService.addEnqueuedNotification(r); runnable.run(); @@ -5090,7 +5246,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.new PostNotificationRunnable(original.getKey(), original.getSbn().getPackageName(), original.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -5114,7 +5270,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.new PostNotificationRunnable(update.getKey(), update.getSbn().getPackageName(), update.getUid(), - mPostNotificationTrackerFactory.newTracker()); + mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -7536,7 +7692,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(update.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10234,7 +10390,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10251,7 +10407,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10268,7 +10424,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), mPostNotificationTrackerFactory.newTracker()); + r.getUid(), mPostNotificationTrackerFactory.newTracker(null)); runnable.run(); waitForIdle(); @@ -10361,7 +10517,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // normal blocked notifications - blocked mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10379,7 +10535,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10392,7 +10548,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats, never()).registerBlocked(any()); @@ -10406,7 +10562,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats, never()).registerBlocked(any()); @@ -10420,7 +10576,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10435,7 +10591,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); @@ -10448,7 +10604,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addEnqueuedNotification(r); mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), r.getUid(), - mPostNotificationTrackerFactory.newTracker()).run(); + mPostNotificationTrackerFactory.newTracker(null)).run(); waitForIdle(); verify(mUsageStats).registerBlocked(any()); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 66c1e35754c5..81c573d8fb1e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -48,6 +48,7 @@ import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.Looper; +import android.os.PowerManager; import android.os.UserHandle; import android.os.UserManager; import android.permission.PermissionManager; @@ -169,6 +170,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(UsageStatsManagerInternal.class), mock(TelecomManager.class), mock(NotificationChannelLogger.class), new TestableFlagResolver(), mock(PermissionManager.class), + mock(PowerManager.class), new NotificationManagerService.PostNotificationTrackerFactory() {}); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index cb984f814f1a..3ff433e546d8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1996,7 +1996,8 @@ public class ActivityRecordTests extends WindowTestsBase { assertTrue(activity.isSnapshotCompatible(snapshot)); - setRotatedScreenOrientationSilently(activity); + doReturn(task.getWindowConfiguration().getRotation() + 1).when(mDisplayContent) + .rotationForActivityInDifferentOrientation(activity); assertFalse(activity.isSnapshotCompatible(snapshot)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 4890f3e6cbf1..bcb0c6b5c269 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -250,9 +250,22 @@ public class ActivityStartInterceptorTest { } @Test - public void testInterceptQuietProfile() { - // GIVEN that the user the activity is starting as is currently in quiet mode + public void testInterceptQuietProfile_keepProfilesRunningEnabled() { + // GIVEN that the user the activity is starting as is currently in quiet mode and + // profiles are kept running when in quiet mode. when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true); + when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true); + + // THEN calling intercept returns false because package also has to be suspended. + assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + } + + @Test + public void testInterceptQuietProfile_keepProfilesRunningDisabled() { + // GIVEN that the user the activity is starting as is currently in quiet mode and + // profiles are stopped when in quiet mode (pre-U behavior, no profile app suspension). + when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true); + when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false); // THEN calling intercept returns true assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); @@ -263,10 +276,28 @@ public class ActivityStartInterceptorTest { } @Test - public void testInterceptQuietProfileWhenPackageSuspended() { + public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningEnabled() { + // GIVEN that the user the activity is starting as is currently in quiet mode, + // the package is suspended and profiles are kept running while in quiet mode. + suspendPackage("com.test.suspending.package"); + when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true); + when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(true); + + // THEN calling intercept returns true + assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); + + // THEN the returned intent is the quiet mode intent + assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID) + .filterEquals(mInterceptor.mIntent)); + } + + @Test + public void testInterceptQuietProfileWhenPackageSuspended_keepProfilesRunningDisabled() { + // GIVEN that the user the activity is starting as is currently in quiet mode, + // the package is suspended and profiles are stopped while in quiet mode. suspendPackage("com.test.suspending.package"); - // GIVEN that the user the activity is starting as is currently in quiet mode when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true); + when(mDevicePolicyManager.isKeepProfilesRunningEnabled()).thenReturn(false); // THEN calling intercept returns true assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, null, 0, 0, null)); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 0044e2e54b5a..4290f4b9ee80 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; @@ -1059,4 +1060,18 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertEquals(0, mAtm.getActivityInterceptorCallbacks().size()); mAtm.mInternal.unregisterActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID); } + + @Test + public void testFocusTopTask() { + final ActivityRecord homeActivity = new ActivityBuilder(mAtm) + .setTask(mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()) + .build(); + final Task pinnedTask = new TaskBuilder(mSupervisor).setCreateActivity(true) + .setWindowingMode(WINDOWING_MODE_PINNED) + .build(); + mAtm.focusTopTask(mDisplayContent.mDisplayId); + + assertTrue(homeActivity.getTask().isFocused()); + assertFalse(pinnedTask.isFocused()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java index b515a9dc0d6b..07d8b8a0c8df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateControllerTests.java @@ -27,7 +27,6 @@ import static org.mockito.ArgumentMatchers.any; import android.content.Context; import android.content.res.Resources; import android.hardware.devicestate.DeviceStateManager; -import android.os.Handler; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; @@ -73,20 +72,19 @@ public class DeviceStateControllerTests { }; mBuilder.setDelegate(mDelegate); mBuilder.build(); - verify(mMockDeviceStateManager).registerCallback(any(), any()); } @Test public void testInitialization() { initialize(true /* supportFold */, true /* supportHalfFolded */); - mTarget.onStateChanged(mOpenDeviceStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); } @Test public void testInitializationWithNoFoldSupport() { initialize(false /* supportFold */, false /* supportHalfFolded */); - mTarget.onStateChanged(mFoldedStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); // Note that the folded state is ignored. assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); } @@ -94,24 +92,24 @@ public class DeviceStateControllerTests { @Test public void testWithFoldSupported() { initialize(true /* supportFold */, false /* supportHalfFolded */); - mTarget.onStateChanged(mOpenDeviceStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); - mTarget.onStateChanged(mFoldedStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); - mTarget.onStateChanged(mHalfFoldedStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]); assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored } @Test public void testWithHalfFoldSupported() { initialize(true /* supportFold */, true /* supportHalfFolded */); - mTarget.onStateChanged(mOpenDeviceStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); - mTarget.onStateChanged(mFoldedStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); - mTarget.onStateChanged(mHalfFoldedStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]); assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState); - mTarget.onStateChanged(mConcurrentDisplayState); + mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState); assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState); } @@ -121,15 +119,15 @@ public class DeviceStateControllerTests { assertEquals(1, mTarget.mDeviceStateCallbacks.size()); assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate)); - mTarget.onStateChanged(mOpenDeviceStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); - mTarget.onStateChanged(mFoldedStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); // The callback should not receive state change when the it is unregistered. mTarget.unregisterDeviceStateCallback(mDelegate); assertTrue(mTarget.mDeviceStateCallbacks.isEmpty()); - mTarget.onStateChanged(mOpenDeviceStates[0]); + mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, mCurrentState); } @@ -195,9 +193,7 @@ public class DeviceStateControllerTests { Resources mockRes = mock(Resources.class); when(mMockContext.getResources()).thenReturn((mockRes)); mockFold(mSupportFold, mSupportHalfFold); - Handler mockHandler = mock(Handler.class); - mTarget = new DeviceStateController(mMockContext, mockHandler, - new WindowManagerGlobalLock()); + mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock()); mTarget.registerDeviceStateCallback(mDelegate, MoreExecutors.directExecutor()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 3ca35ef7cc26..8e015d4d228d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -40,7 +40,9 @@ import static org.junit.Assert.assertEquals; 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.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -51,6 +53,8 @@ import android.app.servertransaction.RefreshCallbackItem; import android.app.servertransaction.ResumeActivityItem; import android.content.ComponentName; import android.content.pm.ActivityInfo.ScreenOrientation; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Configuration.Orientation; import android.hardware.camera2.CameraManager; @@ -84,7 +88,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { private static final String TEST_PACKAGE_2 = "com.test.package.two"; private static final String CAMERA_ID_1 = "camera-1"; private static final String CAMERA_ID_2 = "camera-2"; - + private static final String TEST_PACKAGE_1_LABEL = "testPackage1"; private CameraManager mMockCameraManager; private Handler mMockHandler; private LetterboxConfiguration mLetterboxConfiguration; @@ -133,17 +137,27 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { } @Test - public void testOpenedCameraInSplitScreen_showToast() { + public void testOpenedCameraInSplitScreen_showToast() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); spyOn(mTask); spyOn(mDisplayRotationCompatPolicy); doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode(); doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode(); + final PackageManager mockPackageManager = mock(PackageManager.class); + final ApplicationInfo mockApplicationInfo = mock(ApplicationInfo.class); + when(mContext.getPackageManager()).thenReturn(mockPackageManager); + when(mockPackageManager.getApplicationInfo(anyString(), anyInt())) + .thenReturn(mockApplicationInfo); + + doReturn(TEST_PACKAGE_1_LABEL).when(mockPackageManager) + .getApplicationLabel(mockApplicationInfo); + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); verify(mDisplayRotationCompatPolicy).showToast( - R.string.display_rotation_camera_compat_toast_in_split_screen); + R.string.display_rotation_camera_compat_toast_in_multi_window, + TEST_PACKAGE_1_LABEL); } @Test @@ -157,7 +171,8 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); verify(mDisplayRotationCompatPolicy, never()).showToast( - R.string.display_rotation_camera_compat_toast_in_split_screen); + R.string.display_rotation_camera_compat_toast_in_multi_window, + TEST_PACKAGE_1_LABEL); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 04e1d9c07a07..2a8f0ffc4d49 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -59,10 +59,10 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.devicestate.DeviceStateManager; +import android.os.Handler; import android.os.IBinder; import android.os.PowerManagerInternal; import android.os.SystemClock; -import android.os.Handler; import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.view.DisplayAddress; @@ -518,7 +518,8 @@ public class DisplayRotationTests { mBuilder.build(); configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); - when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true); + when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mMockDisplayContent)) + .thenReturn(true); thawRotation(); @@ -544,7 +545,8 @@ public class DisplayRotationTests { @Test public void testFreezeRotation_reverseRotationDirectionAroundZAxis_yes() throws Exception { mBuilder.build(); - when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(true); + when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mMockDisplayContent)) + .thenReturn(true); freezeRotation(Surface.ROTATION_90); assertEquals(Surface.ROTATION_270, mTarget.getUserRotation()); @@ -553,7 +555,8 @@ public class DisplayRotationTests { @Test public void testFreezeRotation_reverseRotationDirectionAroundZAxis_no() throws Exception { mBuilder.build(); - when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()).thenReturn(false); + when(mDeviceStateController.shouldReverseRotationDirectionAroundZAxis(mMockDisplayContent)) + .thenReturn(false); freezeRotation(Surface.ROTATION_90); assertEquals(Surface.ROTATION_90, mTarget.getUserRotation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index fb29d3adb52b..abf21a57dd40 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -641,6 +641,31 @@ public class RootTaskTests extends WindowTestsBase { // Home split secondary and home task should be invisible. assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, splitSecondary.getVisibility(null /* starting */)); + + // Put another task on top of primary split. + final Task topSplitPrimary = new TaskBuilder(mSupervisor).setParentTask(organizer.mPrimary) + .setCreateActivity(true).build(); + doReturn(false).when(topSplitPrimary).isTranslucent(any()); + // Convert the fullscreen translucent task to opaque. + doReturn(false).when(translucentRootTask).isTranslucent(any()); + translucentRootTask.moveToFront("test"); + // The tasks of primary split are occluded by the fullscreen opaque task. + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + organizer.mPrimary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + topSplitPrimary.getVisibility(null /* starting */)); + // Make primary split root transient-hide. + spyOn(splitPrimary.mTransitionController); + doReturn(true).when(splitPrimary.mTransitionController).isTransientHide( + organizer.mPrimary); + // The split root and its top become visible. + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + organizer.mPrimary.getVisibility(null /* starting */)); + assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, + topSplitPrimary.getVisibility(null /* starting */)); + // The bottom of primary split becomes invisible because it is occluded by topSplitPrimary. + assertEquals(TASK_FRAGMENT_VISIBILITY_INVISIBLE, + splitPrimary.getVisibility(null /* starting */)); } @Test 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 2dd34eb5ac4d..082122e33175 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -406,7 +406,6 @@ public class SizeCompatTests extends WindowTestsBase { clearInvocations(translucentActivity.mLetterboxUiController); // We destroy the first opaque activity - mActivity.setState(DESTROYED, "testing"); mActivity.removeImmediately(); // Check that updateInheritedLetterbox() is invoked again @@ -4654,14 +4653,6 @@ public class SizeCompatTests extends WindowTestsBase { return c; } - private static void resizeDisplay(DisplayContent displayContent, int width, int height) { - displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity, - displayContent.mBaseDisplayPhysicalXDpi, displayContent.mBaseDisplayPhysicalYDpi); - final Configuration c = new Configuration(); - displayContent.computeScreenConfiguration(c); - displayContent.onRequestedOverrideConfigurationChanged(c); - } - private static void setNeverConstrainDisplayApisFlag(@Nullable String value, boolean makeDefault) { DeviceConfig.setProperty(NAMESPACE_CONSTRAIN_DISPLAY_APIS, diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index 192632ce0277..adf3f3976f38 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -18,8 +18,6 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; -import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; - import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; @@ -356,9 +354,4 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { public boolean isGlobalKey(int keyCode) { return false; } - - @Override - public int getLidState() { - return LID_ABSENT; - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index b59f027fec83..11267268a6fa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1346,6 +1346,16 @@ public class TransitionTests extends WindowTestsBase { activity1.setVisible(false); abortTransition.abort(); assertTrue(activity1.isVisible()); + + // The mLaunchTaskBehind flag of an invisible initializing activity should not be cleared. + final Transition noChangeTransition = controller.createTransition(TRANSIT_OPEN); + noChangeTransition.collect(activity1); + activity1.setVisibleRequested(false); + activity1.setState(ActivityRecord.State.INITIALIZING, "test"); + activity1.mLaunchTaskBehind = true; + mWm.mSyncEngine.abort(noChangeTransition.getSyncId()); + noChangeTransition.finishTransition(); + assertTrue(activity1.mLaunchTaskBehind); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index 01ddcca99300..f3d8114b9e94 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -127,6 +127,10 @@ public class WallpaperControllerTests extends WindowTestsBase { public void testWallpaperSizeWithFixedTransform() { // No wallpaper final DisplayContent dc = mDisplayContent; + if (dc.mBaseDisplayHeight == dc.mBaseDisplayWidth) { + // Make sure the size is different when changing orientation. + resizeDisplay(dc, 500, 1000); + } // No wallpaper WSA Surface final WindowState wallpaperWindow = createWallpaperWindow(dc); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index ddc729f773b2..be8ee7832a5d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -71,6 +71,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; import android.hardware.HardwareBuffer; @@ -946,6 +947,14 @@ class WindowTestsBase extends SystemServiceTestsBase { dc.setRotationAnimation(null); } + static void resizeDisplay(DisplayContent displayContent, int width, int height) { + displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity, + displayContent.mBaseDisplayPhysicalXDpi, displayContent.mBaseDisplayPhysicalYDpi); + final Configuration c = new Configuration(); + displayContent.computeScreenConfiguration(c); + displayContent.onRequestedOverrideConfigurationChanged(c); + } + // The window definition for UseTestDisplay#addWindows. The test can declare to add only // necessary windows, that avoids adding unnecessary overhead of unused windows. static final int W_NOTIFICATION_SHADE = TYPE_NOTIFICATION_SHADE; diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java index e57c833e930e..6dcfa6d56ef3 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -21,12 +21,12 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.telecom.IVideoProvider; + import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.android.internal.telecom.IVideoProvider; - /** * A parcelable representation of a conference connection. * @hide @@ -287,6 +287,14 @@ public final class ParcelableConference implements Parcelable { return mCallDirection; } + public String getCallerDisplayName() { + return mCallerDisplayName; + } + + public int getCallerDisplayNamePresentation() { + return mCallerDisplayNamePresentation; + } + public static final @android.annotation.NonNull Parcelable.Creator<ParcelableConference> CREATOR = new Parcelable.Creator<ParcelableConference> () { @Override diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java index 2faecc2e3468..5f0c8d729e74 100644 --- a/telecomm/java/android/telecom/StatusHints.java +++ b/telecomm/java/android/telecom/StatusHints.java @@ -16,14 +16,19 @@ package android.telecom; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.os.Binder; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; + +import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; @@ -33,7 +38,7 @@ import java.util.Objects; public final class StatusHints implements Parcelable { private final CharSequence mLabel; - private final Icon mIcon; + private Icon mIcon; private final Bundle mExtras; /** @@ -48,11 +53,31 @@ public final class StatusHints implements Parcelable { public StatusHints(CharSequence label, Icon icon, Bundle extras) { mLabel = label; - mIcon = icon; + mIcon = validateAccountIconUserBoundary(icon, Binder.getCallingUserHandle()); mExtras = extras; } /** + * @param icon + * @hide + */ + @VisibleForTesting + public StatusHints(@Nullable Icon icon) { + mLabel = null; + mExtras = null; + mIcon = icon; + } + + /** + * + * @param icon + * @hide + */ + public void setIcon(@Nullable Icon icon) { + mIcon = icon; + } + + /** * @return A package used to load the icon. * * @hide @@ -112,6 +137,30 @@ public final class StatusHints implements Parcelable { return 0; } + /** + * Validates the StatusHints image icon to see if it's not in the calling user space. + * Invalidates the icon if so, otherwise returns back the original icon. + * + * @param icon + * @return icon (validated) + * @hide + */ + public static Icon validateAccountIconUserBoundary(Icon icon, UserHandle callingUserHandle) { + // Refer to Icon#getUriString for context. The URI string is invalid for icons of + // incompatible types. + if (icon != null && (icon.getType() == Icon.TYPE_URI + || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) { + String encodedUser = icon.getUri().getEncodedUserInfo(); + // If there is no encoded user, the URI is calling into the calling user space + if (encodedUser != null) { + int userId = Integer.parseInt(encodedUser); + // Do not try to save the icon if the user id isn't in the calling user space. + if (userId != callingUserHandle.getIdentifier()) return null; + } + } + return icon; + } + @Override public void writeToParcel(Parcel out, int flags) { out.writeCharSequence(mLabel); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 188894339f6a..5b1c6b1c88b8 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -10318,7 +10318,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP); sDefaults.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, 300); - sDefaults.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, 0); + sDefaults.putInt(KEY_PREFERRED_IKE_PROTOCOL_INT, -1); // Default wifi configurations. sDefaults.putAll(Wifi.getDefaults()); sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp index 27640a5f51bc..84845c69fb27 100644 --- a/tests/InputMethodStressTest/Android.bp +++ b/tests/InputMethodStressTest/Android.bp @@ -30,7 +30,6 @@ android_test { ], test_suites: [ "general-tests", - "vts", ], data: [ ":SimpleTestIme", diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml index 62eee0270cac..e890c9974882 100644 --- a/tests/InputMethodStressTest/AndroidManifest.xml +++ b/tests/InputMethodStressTest/AndroidManifest.xml @@ -19,8 +19,7 @@ package="com.android.inputmethod.stresstest"> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <application> - <activity android:name=".ImeStressTestUtil$TestActivity" - android:configChanges="orientation|screenSize"/> + <activity android:name=".ImeStressTestUtil$TestActivity"/> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml index bedf0990a188..bfebb42ad244 100644 --- a/tests/InputMethodStressTest/AndroidTest.xml +++ b/tests/InputMethodStressTest/AndroidTest.xml @@ -19,6 +19,7 @@ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" /> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> </target_preparer> diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java index 3d257b29287f..807f0c63668c 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java @@ -199,8 +199,7 @@ public final class AutoShowTest { Collections.singletonList(REQUEST_FOCUS_ON_CREATE)); TestActivity firstActivity = TestActivity.start(intent1); // Show Ime with InputMethodManager to ensure the keyboard is on. - boolean succ = callOnMainSync(firstActivity::showImeWithInputMethodManager); - assertThat(succ).isTrue(); + callOnMainSync(firstActivity::showImeWithInputMethodManager); SystemClock.sleep(1000); mInstrumentation.waitForIdleSync(); @@ -264,8 +263,7 @@ public final class AutoShowTest { Collections.singletonList(REQUEST_FOCUS_ON_CREATE)); ImeStressTestUtil.TestActivity secondActivity = activity.startSecondTestActivity(intent2); // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity - boolean succ = callOnMainSync(secondActivity::showImeWithInputMethodManager); - assertThat(succ).isTrue(); + callOnMainSync(secondActivity::showImeWithInputMethodManager); SystemClock.sleep(1000); mInstrumentation.waitForIdleSync(); // Close the second activity diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java index 299cbf1a84c7..0c267b27490b 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java @@ -26,8 +26,6 @@ import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowA import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden; import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown; -import static com.google.common.truth.Truth.assertThat; - import android.content.Intent; import android.platform.test.annotations.RootPermissionTest; import android.platform.test.rule.UnlockScreenRule; @@ -83,14 +81,11 @@ public final class DefaultImeVisibilityTest { ImeStressTestUtil.TestActivity activity = ImeStressTestUtil.TestActivity.start(intent); EditText editText = activity.getEditText(); for (int i = 0; i < NUM_TEST_ITERATIONS; i++) { - - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isTrue(); + callOnMainSync(activity::showImeWithInputMethodManager); verifyWindowAndViewFocus(editText, true, true); waitOnMainUntilImeIsShown(editText); - boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager); - assertThat(hideResult).isTrue(); + callOnMainSync(activity::hideImeWithInputMethodManager); waitOnMainUntilImeIsHidden(editText); } } diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java index 7632ab08b655..5c0212400ff1 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java @@ -102,12 +102,10 @@ public final class ImeOpenCloseStressTest { for (int i = 0; i < iterNum; i++) { String msgPrefix = "Iteration #" + i + " "; Log.i(TAG, msgPrefix + "start"); - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isEqualTo(!(hasUnfocusableWindowFlags(activity))); + callOnMainSync(activity::showImeWithInputMethodManager); verifyShowBehavior(activity); - boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager); - assertThat(hideResult).isEqualTo(!(hasUnfocusableWindowFlags(activity))); + callOnMainSync(activity::hideImeWithInputMethodManager); verifyHideBehavior(activity); } @@ -128,14 +126,12 @@ public final class ImeOpenCloseStressTest { for (int i = 0; i < NUM_TEST_ITERATIONS; i++) { String msgPrefix = "Iteration #" + i + " "; Log.i(TAG, msgPrefix + "start"); - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isTrue(); + callOnMainSync(activity::showImeWithInputMethodManager); waitOnMainUntil( msgPrefix + "IME should be visible", () -> !activity.isAnimating() && isImeShown(editText)); - boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager); - assertThat(hideResult).isTrue(); + callOnMainSync(activity::hideImeWithInputMethodManager); waitOnMainUntil( msgPrefix + "IME should be hidden", () -> !activity.isAnimating() && !isImeShown(editText)); @@ -156,17 +152,13 @@ public final class ImeOpenCloseStressTest { List<Integer> intervals = new ArrayList<>(); for (int i = 10; i < 100; i += 10) intervals.add(i); for (int i = 100; i < 1000; i += 50) intervals.add(i); - boolean firstHide = false; for (int intervalMillis : intervals) { String msgPrefix = "Interval = " + intervalMillis + " "; Log.i(TAG, msgPrefix + " start"); - boolean hideResult = callOnMainSync(activity::hideImeWithInputMethodManager); - assertThat(hideResult).isEqualTo(firstHide); - firstHide = true; + callOnMainSync(activity::hideImeWithInputMethodManager); SystemClock.sleep(intervalMillis); - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isTrue(); + callOnMainSync(activity::showImeWithInputMethodManager); verifyShowBehavior(activity); } } @@ -247,8 +239,7 @@ public final class ImeOpenCloseStressTest { TestActivity activity = TestActivity.start(intent); // Show InputMethodManager without requesting focus - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isFalse(); + callOnMainSync(activity::showImeWithInputMethodManager); int windowFlags = activity.getWindow().getAttributes().flags; EditText editText = activity.getEditText(); @@ -474,8 +465,7 @@ public final class ImeOpenCloseStressTest { Collections.singletonList(REQUEST_FOCUS_ON_CREATE)); TestActivity activity = TestActivity.start(intent1); // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isEqualTo(!(hasUnfocusableWindowFlags(activity))); + callOnMainSync(activity::showImeWithInputMethodManager); Thread.sleep(1000); verifyShowBehavior(activity); @@ -493,6 +483,7 @@ public final class ImeOpenCloseStressTest { verifyShowBehavior(activity); } + // TODO: Add tests for activities that don't handle the rotation. @Test public void testRotateScreenWithKeyboardOn() throws Exception { Intent intent = @@ -502,8 +493,7 @@ public final class ImeOpenCloseStressTest { Collections.singletonList(REQUEST_FOCUS_ON_CREATE)); TestActivity activity = TestActivity.start(intent); // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity - boolean showResult = callOnMainSync(activity::showImeWithInputMethodManager); - assertThat(showResult).isEqualTo(!(hasUnfocusableWindowFlags(activity))); + callOnMainSync(activity::showImeWithInputMethodManager); Thread.sleep(2000); verifyShowBehavior(activity); @@ -514,14 +504,14 @@ public final class ImeOpenCloseStressTest { Thread.sleep(1000); Log.i(TAG, "Rotate screen right"); assertThat(uiDevice.isNaturalOrientation()).isFalse(); - verifyShowBehavior(activity); + verifyRotateBehavior(activity); uiDevice.setOrientationLeft(); uiDevice.waitForIdle(); Thread.sleep(1000); Log.i(TAG, "Rotate screen left"); assertThat(uiDevice.isNaturalOrientation()).isFalse(); - verifyShowBehavior(activity); + verifyRotateBehavior(activity); uiDevice.setOrientationNatural(); uiDevice.waitForIdle(); @@ -569,4 +559,36 @@ public final class ImeOpenCloseStressTest { waitOnMainUntilImeIsShown(editText); } } + + private static void verifyRotateBehavior(TestActivity activity) { + // Get the new TestActivity after recreation. + TestActivity newActivity = TestActivity.getLastCreatedInstance(); + assertThat(newActivity).isNotNull(); + assertThat(newActivity).isNotEqualTo(activity); + + EditText newEditText = newActivity.getEditText(); + int softInputMode = newActivity.getWindow().getAttributes().softInputMode; + int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE; + + if (hasUnfocusableWindowFlags(newActivity)) { + verifyImeAlwaysHiddenWithWindowFlagSet(newActivity); + return; + } + + if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) { + // After rotation, the keyboard would be hidden only when the flag is + // SOFT_INPUT_STATE_ALWAYS_HIDDEN. However, SOFT_INPUT_STATE_HIDDEN is different because + // it requires appending SOFT_INPUT_IS_FORWARD_NAVIGATION flag, which won't be added + // when rotating the devices (rotating doesn't navigate forward to the next app window.) + verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/ + true); + waitOnMainUntilImeIsHidden(newEditText); + + } else { + // Other cases, keyboard would be shown. + verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/ + true); + waitOnMainUntilImeIsShown(newEditText); + } + } } diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java index e16c9152df3e..f3c81942ae42 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java @@ -45,6 +45,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.ThrowingRunnable; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -296,6 +297,8 @@ public final class ImeStressTestUtil { private static final String TAG = "ImeStressTestUtil.TestActivity"; private EditText mEditText; private boolean mIsAnimating; + private static WeakReference<TestActivity> sLastCreatedInstance = + new WeakReference<>(null); private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -336,6 +339,7 @@ public final class ImeStressTestUtil { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, "onCreate()"); + sLastCreatedInstance = new WeakReference<>(this); boolean isUnfocusableView = getIntent().getBooleanExtra(UNFOCUSABLE_VIEW, false); boolean requestFocus = getIntent().getBooleanExtra(REQUEST_FOCUS_ON_CREATE, false); int softInputFlags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0); @@ -378,6 +382,12 @@ public final class ImeStressTestUtil { } } + /** Get the last created TestActivity instance. */ + @Nullable + public static TestActivity getLastCreatedInstance() { + return sLastCreatedInstance.get(); + } + /** Show IME with InputMethodManager. */ public boolean showImeWithInputMethodManager() { boolean showResult = diff --git a/tests/SilkFX/res/layout/gainmap_transform_test.xml b/tests/SilkFX/res/layout/gainmap_transform_test.xml new file mode 100644 index 000000000000..5aeb53661cbc --- /dev/null +++ b/tests/SilkFX/res/layout/gainmap_transform_test.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<com.android.test.silkfx.hdr.GainmapTransformsTest xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <Button + android:id="@+id/original" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Original" /> + + <Button + android:id="@+id/scaled" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Scaled (1/3)" /> + + <Button + android:id="@+id/rotate_90" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Rotate 90" /> + + <Button + android:id="@+id/rotate_90_scaled" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Rot90+Scale" /> + + <Button + android:id="@+id/crop" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Crop" /> + + <Button + android:id="@+id/crop_200" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="Crop 200" /> + + </LinearLayout> + + <TextView + android:id="@+id/source_info" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="1"> + + <TextView + android:id="@+id/sdr_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ImageView + android:id="@+id/sdr_source" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="8dp" + android:scaleType="fitStart" /> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="match_parent" + android:orientation="vertical" + android:layout_weight="1"> + + <TextView + android:id="@+id/gainmap_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ImageView + android:id="@+id/gainmap" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="8dp" + android:scaleType="fitStart" /> + </LinearLayout> + + </LinearLayout> + +</com.android.test.silkfx.hdr.GainmapTransformsTest>
\ No newline at end of file diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt index a6cdbb9865bc..59a6078376cf 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt @@ -55,7 +55,9 @@ private val AllDemos = listOf( Demo("Color Grid", R.layout.color_grid), Demo("Gradient Sweep", R.layout.gradient_sweep), Demo("Gainmap Image", R.layout.gainmap_image), - Demo("Gainmap Decode Test", R.layout.gainmap_decode_test, commonControls = false) + Demo("Gainmap Decode Test", R.layout.gainmap_decode_test, commonControls = false), + Demo("Gainmap Transform Test", R.layout.gainmap_transform_test, + commonControls = false) )), DemoGroup("Materials", listOf( Demo("Glass", GlassActivity::class), diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt index a004fb5a4305..585320aee615 100644 --- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt @@ -17,7 +17,12 @@ package com.android.test.silkfx.hdr import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.ColorMatrixColorFilter +import android.graphics.Gainmap import android.graphics.ImageDecoder +import android.graphics.Paint import android.graphics.Rect import android.util.AttributeSet import android.widget.Button @@ -34,6 +39,25 @@ enum class DecodeMode { CropedSquaredScaled33 } +fun gainmapVisualizer(gainmap: Gainmap): Bitmap { + val map = gainmap.gainmapContents + val gainmapVisualizer = Bitmap.createBitmap(map.width, map.height, + Bitmap.Config.ARGB_8888) + val canvas = Canvas(gainmapVisualizer!!) + val paint = Paint() + paint.colorFilter = ColorMatrixColorFilter( + floatArrayOf( + 0f, 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 1f, 0f, + 0f, 0f, 0f, 0f, 255f + ) + ) + canvas.drawBitmap(map, 0f, 0f, paint) + canvas.setBitmap(null) + return gainmapVisualizer +} + class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { private fun decode(mode: DecodeMode) { @@ -71,7 +95,7 @@ class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(c } } - val gainmapContents = gainmapImage.gainmap!!.gainmapContents!! + val gainmapContents = gainmapImage.gainmap?.let { gainmapVisualizer(it) } val sdrBitmap = gainmapImage.also { it.gainmap = null } findViewById<ImageView>(R.id.sdr_source)!!.setImageBitmap(sdrBitmap) @@ -80,7 +104,7 @@ class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(c findViewById<ImageView>(R.id.gainmap)!!.setImageBitmap(gainmapContents) findViewById<TextView>(R.id.gainmap_label)!!.text = - "Gainmap Size: ${gainmapContents.width}x${gainmapContents.height}" + "Gainmap Size: ${gainmapContents?.width ?: 0}x${gainmapContents?.height ?: 0}" } override fun onFinishInflate() { diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt new file mode 100644 index 000000000000..20984fae2133 --- /dev/null +++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt @@ -0,0 +1,116 @@ +/* + * 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.test.silkfx.hdr + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.ImageDecoder +import android.graphics.Matrix +import android.util.AttributeSet +import android.widget.Button +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.android.test.silkfx.R + +class GainmapTransformsTest(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { + + private val sourceImage = loadSample() + + private fun loadSample(): Bitmap { + val source = ImageDecoder.createSource(resources.assets, + "gainmaps/${context.assets.list("gainmaps")!![0]}") + + return ImageDecoder.decodeBitmap(source) { decoder, info, source -> + decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE + } + } + + private fun process(transform: (Bitmap) -> Bitmap) { + val result = transform(sourceImage) + + val gainmapContents = result.gainmap?.let { gainmapVisualizer(it) } + val sdrBitmap = result.also { it.gainmap = null } + + findViewById<ImageView>(R.id.sdr_source)!!.setImageBitmap(sdrBitmap) + findViewById<TextView>(R.id.sdr_label)!!.text = + "SDR Size: ${sdrBitmap.width}x${sdrBitmap.height}" + + findViewById<ImageView>(R.id.gainmap)!!.setImageBitmap(gainmapContents) + findViewById<TextView>(R.id.gainmap_label)!!.text = + "Gainmap Size: ${gainmapContents?.width ?: 0}x${gainmapContents?.height ?: 0}" + } + + override fun onFinishInflate() { + super.onFinishInflate() + val sourceInfo = findViewById<TextView>(R.id.source_info)!! + sourceInfo.text = "Original size ${sourceImage.width}x${sourceImage.height}" + process { it.copy(Bitmap.Config.ARGB_8888, false) } + + findViewById<Button>(R.id.original)!!.setOnClickListener { + process { it.copy(Bitmap.Config.ARGB_8888, false) } + } + + findViewById<Button>(R.id.scaled)!!.setOnClickListener { + process { Bitmap.createScaledBitmap(it, it.width / 3, it.height / 3, true) } + } + + findViewById<Button>(R.id.rotate_90)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + + val m = Matrix() + m.setRotate(90.0f, (width / 2).toFloat(), (height / 2).toFloat()) + Bitmap.createBitmap(it, 0, 0, width, height, m, false) + } + } + + findViewById<Button>(R.id.rotate_90_scaled)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + + val m = Matrix() + m.setRotate(90.0f, (width / 2).toFloat(), (height / 2).toFloat()) + m.preScale(.3f, .3f) + Bitmap.createBitmap(it, 0, 0, width, height, m, false) + } + } + + findViewById<Button>(R.id.crop)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + Bitmap.createBitmap(it, width / 2, height / 2, + width / 4, height / 4, null, false) + } + } + + findViewById<Button>(R.id.crop_200)!!.setOnClickListener { + process { + val width: Int = it.width + val height: Int = it.height + + val m = Matrix() + m.setRotate(200.0f, (width / 2).toFloat(), (height / 2).toFloat()) + Bitmap.createBitmap(it, width / 2, height / 2, + width / 4, height / 4, m, false) + } + } + } +}
\ No newline at end of file diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 4123f8070e36..960b57cb632a 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -118,6 +118,7 @@ import java.util.UUID; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagementServiceTest { + private static final String CONTEXT_ATTRIBUTION_TAG = "VCN"; private static final String TEST_PACKAGE_NAME = VcnManagementServiceTest.class.getPackage().getName(); private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2"; @@ -177,6 +178,7 @@ public class VcnManagementServiceTest { 0 /* carrierId */, 0 /* profileClass */); + private final Context mMockContextWithoutAttributionTag = mock(Context.class); private final Context mMockContext = mock(Context.class); private final VcnManagementService.Dependencies mMockDeps = mock(VcnManagementService.Dependencies.class); @@ -202,6 +204,10 @@ public class VcnManagementServiceTest { private final IBinder mMockIBinder = mock(IBinder.class); public VcnManagementServiceTest() throws Exception { + doReturn(mMockContext) + .when(mMockContextWithoutAttributionTag) + .createAttributionContext(CONTEXT_ATTRIBUTION_TAG); + setupSystemService( mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); setupSystemService( @@ -249,7 +255,7 @@ public class VcnManagementServiceTest { doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk(); setupMockedCarrierPrivilege(true); - mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); + mVcnMgmtSvc = new VcnManagementService(mMockContextWithoutAttributionTag, mMockDeps); setupActiveSubscription(TEST_UUID_1); doReturn(mMockIBinder).when(mMockPolicyListener).asBinder(); diff --git a/wifi/tests/src/android/net/wifi/nl80211/OWNERS b/wifi/tests/src/android/net/wifi/nl80211/OWNERS new file mode 100644 index 000000000000..8a75e25cb2f6 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/nl80211/OWNERS @@ -0,0 +1 @@ +kumachang@google.com |